使用awk打印从n到最后一列的所有列

这一行一直工作到第二个字段中出现空白。

svn status | grep '\!' | gawk '{print $2;}' > removedProjs

有没有办法让awk打印所有2美元或更大的东西?(3、4美元. .直到我们不再有专栏了?)

我想我应该补充一点,我正在使用Cygwin在Windows环境中执行此操作。

604551 次浏览

您可以使用for循环来循环打印字段$2到$NF(表示行上字段数量的内置变量)。

< p >编辑: 因为"print"附加了一个换行符,你会想要缓冲结果:

awk '{out=""; for(i=2;i<=NF;i++){out=out" "$i}; print out}'

或者,使用printf:

awk '{for(i=2;i<=NF;i++){printf "%s ", $i}; printf "\n"}'

这样能行吗?

awk '{print substr($0,length($1)+1);}' < file

它在前面留下了一些空白。

打印所有列:

awk '{print $0}' somefile

打印除第一列以外的所有内容:

awk '{$1=""; print $0}' somefile

打印除前两列以外的所有内容:

awk '{$1=$2=""; print $0}' somefile
awk '{out=$2; for(i=3;i<=NF;i++){out=out" "$i}; print out}'

我的答案是基于威尔的那个,但我注意到它在打印第二列(和其余部分)之前以空白开始。因为我只有1个声望点,所以我不能评论它,所以这是一个新的答案:

以“out”作为第二列开始,然后添加所有其他列(如果存在)。只要有第二列,这就很好。

这里有一个使用cut的简单的回答的重复问题:

 svn status |  grep '\!' | cut -d\  -f2-

-d指定delimeter (空间)-f指定列列表(从2号开始)

echo "1 2 3 4 5 6" | awk '{ $NF = ""; print $0}'

它使用awk打印除最后一个字段以外的所有字段

以下是我在所有推荐中更喜欢的:

从第六列到最后一列打印。

ls -lthr | awk '{out=$6; for(i=7;i<=NF;i++){out=out" "$i}; print out}'

ls -lthr | awk '{ORS=" "; for(i=6;i<=NF;i++) print $i;print "\n"}'

打印从#2开始的列(输出在开始时没有尾随空格):

ls -l | awk '{sub(/[^ ]+ /, ""); print $0}'

我个人尝试了上面提到的所有答案,但大多数都有点复杂或不正确。在我看来,最简单的方法是:

awk -F" " '{ for (i=4; i<=NF; i++) print $i }'
  1. 其中-F" "定义了awk使用的分隔符。在我的例子中是空格,它也是awk的默认分隔符。这意味着-F" "可以被忽略。

  2. 其中NF定义字段/列的总数。因此,循环将从第4个字段开始,一直到最后一个字段/列。

  3. 其中$N检索第N个字段的值。因此,print $i将根据循环计数打印当前字段/列。

Perl:

@m=`ls -ltr dir | grep ^d | awk '{print \$6,\$7,\$8,\$9}'`;
foreach $i (@m)
{
print "$i\n";


}

这让我非常恼火,我坐下来写了一个类似__abc0的字段规范解析器,用GNU Awk 3.1.7测试。

首先,创建一个名为pfcut的新的Awk库脚本。

sudo nano /usr/share/awk/pfcut

然后,粘贴下面的脚本,并保存。之后,是这样的用法:

$ echo "t1 t2 t3 t4 t5 t6 t7" | awk -f pfcut --source '/^/ { pfcut("-4"); }'
t1 t2 t3 t4


$ echo "t1 t2 t3 t4 t5 t6 t7" | awk -f pfcut --source '/^/ { pfcut("2-"); }'
t2 t3 t4 t5 t6 t7


$ echo "t1 t2 t3 t4 t5 t6 t7" | awk -f pfcut --source '/^/ { pfcut("-2,4,6-"); }'
t1 t2 t4 t6 t7

为了避免键入所有这些,我想最好的办法(参见在awk启动时自动加载用户函数?- Unix &Linux Stack Exchange)是为~/.bashrc添加一个别名;例如:

$ echo "alias awk-pfcut='awk -f pfcut --source'" >> ~/.bashrc
$ source ~/.bashrc     # refresh bash aliases

... 然后你可以调用:

$ echo "t1 t2 t3 t4 t5 t6 t7" | awk-pfcut '/^/ { pfcut("-2,4,6-"); }'
t1 t2 t4 t6 t7

下面是pfcut脚本的源代码:

# pfcut - print fields like cut
#
# sdaau, GNU GPL
# Nov, 2013


function spfcut(formatstring)
{
# parse format string
numsplitscomma = split(formatstring, fsa, ",");
numspecparts = 0;
split("", parts); # clear/initialize array (for e.g. `tail` piping into `awk`)
for(i=1;i<=numsplitscomma;i++) {
commapart=fsa[i];
numsplitsminus = split(fsa[i], cpa, "-");
# assume here a range is always just two parts: "a-b"
# also assume user has already sorted the ranges
#print numsplitsminus, cpa[1], cpa[2]; # debug
if(numsplitsminus==2) {
if ((cpa[1]) == "") cpa[1] = 1;
if ((cpa[2]) == "") cpa[2] = NF;
for(j=cpa[1];j<=cpa[2];j++) {
parts[numspecparts++] = j;
}
} else parts[numspecparts++] = commapart;
}
n=asort(parts); outs="";
for(i=1;i<=n;i++) {
outs = outs sprintf("%s%s", $parts[i], (i==n)?"":OFS);
#print(i, parts[i]); # debug
}
return outs;
}


function pfcut(formatstring) {
print spfcut(formatstring);
}

如果你正在使用Bash,你可以使用尽可能多的“x”作为你希望丢弃的元素,如果它们没有转义,它会忽略多个空格。

while read x b; do echo "$b"; done < filename
ls -la | awk '{o=$1" "$3; for (i=5; i<=NF; i++) o=o" "$i; print o }'

from 这个答案还不错,但是自然间距消失了 请将它与这个进行比较:

ls -la | cut -d\  -f4-

然后你就会看到区别了。

即使是基于这个问题的答案ls -la | awk '{$1=$2=""; print}'也没有保留格式。

因此,我将使用以下,它还允许显式选择列在开始:

ls -la | cut -d\  -f1,4-

注意,每个空格也计算列,例如在下面,列1和列3是空的,2是INFO, 4是:

$ echo " INFO  2014-10-11 10:16:19  main " | cut -d\  -f1,3


$ echo " INFO  2014-10-11 10:16:19  main " | cut -d\  -f2,4
INFO 2014-10-11
$

如果需要用任意delimeter打印特定列:

awk '{print $3 "  " $4}'

# 3 # 4上校上校

awk '{print $3 "anything" $4}'

坳anythingcol # 4 # 3

因此,如果您在一列中有空格,它将是两列,但您可以使用任何分隔符连接它或不使用它。

如果你想要格式化文本,用echo链接你的命令并使用$0打印最后一个字段。

例子:

for i in {8..11}; do
s1="$i"
s2="str$i"
s3="str with spaces $i"
echo -n "$s1 $s2" | awk '{printf "|%3d|%6s",$1,$2}'
echo -en "$s3" | awk '{printf "|%-19s|\n", $0}'
done

打印:

|  8|  str8|str with spaces 8  |
|  9|  str9|str with spaces 9  |
| 10| str10|str with spaces 10 |
| 11| str11|str with spaces 11 |

Perl的解决方案:

perl -lane 'splice @F,0,1; print join " ",@F' file

使用以下命令行选项:

  • -n循环输入文件的每一行,不会自动打印每一行

  • -l在处理之前删除换行符,并在处理之后将它们添加回来

  • -a自动拆分模式-将输入行拆分到@F数组中。默认为空格分割

  • -e执行perl代码

splice @F,0,1干净地从@F数组中删除第0列

join " ",@F连接@F数组的元素,在每个元素之间使用一个空格


Python的解决方案:

python -c "import sys;[sys.stdout.write(' '.join(line.split()[1:]) + '\n') for line in sys.stdin]" < file

使用awk的大多数解决方案都留有空间。这里的选项避免了这个问题。

选项1

一个简单的切割解决方案(只适用于单个分隔符):

command | cut -d' ' -f3-

选项2

强制awk重新计算有时会通过删除第一个字段来删除添加的前导空格(OFS)(适用于某些版本的awk):

command | awk '{ $1=$2="";$0=$0;} NF=NF'

选项3

打印用printf格式化的每个字段将提供更多的控制:

$ in='    1    2  3     4   5   6 7     8  '
$ echo "$in"|awk -v n=2 '{ for(i=n+1;i<=NF;i++) printf("%s%s",$i,i==NF?RS:OFS);}'
3 4 5 6 7 8

但是,所有之前的答案都将字段之间的所有重复FS更改为OFS。让我们创建一些选项,不做这些。

选项4(推荐)

使用sub循环删除前面的字段和分隔符 并且使用FS值而不是space(可以更改).
更易于移植,并且不会触发FS到OFS的更改: ^[FS]*将接受前导空格的输入。

$ in='    1    2  3     4   5   6 7     8  '
$ echo "$in" | awk '{ n=2; a="^["FS"]*[^"FS"]+["FS"]+";
for(i=1;i<=n;i++) sub( a , "" , $0 ) } 1 '
3     4   5   6 7     8

选择5

很有可能构建一个不添加额外(前导或尾随)空白的解决方案,并使用GNU awk中的gensub函数保留现有空白,如下所示:

$ echo '    1    2  3     4   5   6 7     8  ' |
awk -v n=2 'BEGIN{ a="^["FS"]*"; b="([^"FS"]+["FS"]+)"; c="{"n"}"; }
{ print(gensub(a""b""c,"",1)); }'
3     4   5   6 7     8

它也可以用于交换给定计数n的一组字段:

$ echo '    1    2  3     4   5   6 7     8  ' |
awk -v n=2 'BEGIN{ a="^["FS"]*"; b="([^"FS"]+["FS"]+)"; c="{"n"}"; }
{
d=gensub(a""b""c,"",1);
e=gensub("^(.*)"d,"\\1",1,$0);
print("|"d"|","!"e"!");
}'
|3     4   5   6 7     8  | !    1    2  !

当然,在这种情况下,OFS将用于分隔行的两个部分,并且仍然打印字段后面的空白。

注意: [FS]*用于在输入行中允许前导空格。

Awk示例在这里看起来很复杂,这里是简单的Bash shell语法:

command | while read -a cols; do echo ${cols[@]:1}; done

其中1是你的__abc1列从0开始计数。


例子

给定文件(in.txt)的内容:

c1
c1 c2
c1 c2 c3
c1 c2 c3 c4
c1 c2 c3 c4 c5

输出如下:

$ while read -a cols; do echo ${cols[@]:1}; done < in.txt


c2
c2 c3
c2 c3 c4
c2 c3 c4 c5

我对这里提供的任何awk解决方案都不满意,因为我想提取前几列,然后打印其余的,所以我转而使用perl。下面的代码提取了前两列,并按原样显示其余部分:

echo -e "a  b  c  d\te\t\tf g" | \
perl -ne 'my @f = split /\s+/, $_, 3; printf "first: %s second: %s rest: %s", @f;'

与来自克里斯Koknatperl解决方案相比,其优点是实际上只从输入字符串中分离出前n个元素;字符串的其余部分根本没有被分割,因此保持完整。我的示例混合使用空格和制表符来演示这一点。

要改变要提取的列的数量,将示例中的3替换为n+1。

awk函数返回$0的子字符串,包含从beginend的字段:

function fields(begin, end,    b, e, p, i) {
b = 0; e = 0; p = 0;
for (i = 1; i <= NF; ++i) {
if (begin == i) { b = p; }
p += length($i);
e = p;
if (end == i) { break; }
p += length(FS);
}
return substr($0, b + 1, e - b);
}

获取从字段3开始的所有内容:

tail = fields(3);

获取$0中覆盖字段3到5的部分:

middle = fields(3, 5);

函数形参表中的b, e, p, i无意义只是声明局部变量的awk方式。

awk '{ for(i=3; i<=NF; ++i) printf $i""FS; print "" }'

lauhub提出了这个正确、简单、快速的解决方案在这里

我想将建议的答案扩展到字段可能由几个空格分隔的情况——我认为OP不使用cut的原因。

我知道OP问的是awk,但这里可以用sed方法(例如从第5列打印到最后一列):

  • 纯sed方法

      sed -r 's/^\s*(\S+\s+){4}//' somefile
    

    解释:

    • s///是执行替换的标准命令
    • ^\s*匹配行首任何连续的空格
    • \S+\s+表示一列数据(非空白字符后跟空白字符)
    • (){4}表示模式重复4次。
  • sed and cut

      sed -r 's/^\s+//; s/\s+/\t/g' somefile | cut -f5-
    

    只要用一个标签替换连续的空格;

  • tr and cut: tr也可以用于带有-s选项的连续挤压字符

      tr -s [:blank:] <somefile | cut -d' ' -f5-
    

投票最多的回答由zed_0xff对我没用。

我有一个日志,其中$5与IP地址可以是更多的文本或没有文本。我需要从IP地址到行尾的所有内容,如果有任何在$5之后。在我的例子中,这实际上是在一个awk程序中,而不是awk一行程序,因此awk必须解决这个问题。当我试图删除前4个字段使用zed_0xff提出的解决方案:

echo "  7 27.10.16. Thu 11:57:18 37.244.182.218" | awk '{$1=$2=$3=$4=""; printf "[%s]\n", $0}'

它吐出错误和无用的回应(我添加了[..]以示范):

[    37.244.182.218 one two three]

甚至有人建议将substr与这个错误的答案结合起来,但这只会使事情复杂化。它没有提供任何改进。

相反,如果列的宽度是固定的,直到需要切割点和awk,正确答案是:

echo "  7 27.10.16. Thu 11:57:18 37.244.182.218" | awk '{printf "[%s]\n", substr($0,28)}'

它产生所需的输出:

[37.244.182.218 one two three]

在这里给出的所有其他答案以及在相关问题中给出的各种可能的FS值都以各种方式失败。有些在开头和/或结尾留下空白,有些将每个FS转换为OFS,有些依赖于仅当FS为默认值时才适用的语义,有些依赖于在括号表达式中否定FS,这将在给定多字符FS时失败,等等。

为了对任何FS都健壮地做到这一点,使用GNU awk的第4个参数split():

$ cat tst.awk
{
split($0,flds,FS,seps)
for ( i=n; i<=NF; i++ ) {
printf "%s%s", flds[i], seps[i]
}
print ""
}

$ printf 'a   b c    d\n' | awk -v n=3 -f tst.awk
c    d


$ printf ' a   b c    d\n' | awk -v n=3 -f tst.awk
c    d


$ printf ' a   b c    d\n' | awk -v n=3 -F'[ ]' -f tst.awk
b c    d


$ printf ' a   b c    d\n' | awk -v n=3 -F'[ ]+' -f tst.awk
b c    d


$ printf 'a###b###c###d\n' | awk -v n=3 -F'###' -f tst.awk
c###d


$ printf '###a###b###c###d\n' | awk -v n=3 -F'###' -f tst.awk
b###c###d

注意,我上面使用split()是因为它的3rg arg是字段分隔符,而不是像match()的第二个arg那样只是一个regexp。区别在于,字段分隔符比regexp具有额外的语义,例如当分隔符是单个空白字符时,跳过前导和/或尾随空白—如果您想使用while(match())循环或任何形式的*sub()来模拟上述情况,那么您需要编写代码来实现这些语义,而split()已经为您实现了这些语义。

更新:

如果你想在保留空格和制表符的同时不使用任何函数调用,那么:

echo  "    1   2  33  4444 555555 \t6666666    " |


{m,g}awk ++NF FS='^[ \t]*[^ \t]*[ \t]+|[ \t]+$' OFS=

2  33  4444 555555     6666666

===================

你可以让它更直接:

 svn status | [m/g]awk   '/!/*sub("^[^ \t]*[ \t]+",_)'


svn status |   [n]awk '(/!/)*sub("^[^ \t]*[ \t]+",_)'

自动处理管道中前面的grep,以及在删除$1后删除额外的FS,额外的好处是保留原始输入的其余部分而不是用空格覆盖制表符(除非这是想要的效果)

如果你非常确定$1不包含需要正则转义的特殊字符,那么它甚至更容易:

mawk         '/!/*sub($!_"[ \t]+",_)'
gawk -c/P/e '/!/*sub($!_"""[ \t]+",_)'

或者如果你更喜欢自定义FS+OFS来处理所有问题:

mawk 'NF*=/!/' FS='^[^ \t]*[ \t]+' OFS='' # this version uses OFS

这应该是一个相当全面的__abc0 -field子字符串提取函数

  • 根据输入范围返回$0的子字符串,包容
  • 夹入超出范围的值,
  • handle < >强可变长度< / >强字段SEPs
  • 加速治疗::
  • 完全没有输入,直接返回$0
  • 输入值导致有保证的空字符串("")
  • FROM-field == 1
  • FS = "",将$0按单个字符分割 (因此FROM <(_)>TO <(__)>字段表现为cut -c而不是cut -f)
  • 原始的$0恢复,w/o覆盖FS seps与OFS

|

 {m,g}awk '{
2         print "\n|---BEFORE-------------------------\n"
3         ($0) "\n|----------------------------\n\n  ["
4         fld2(2, 5) "]\n  [" fld2(3) "]\n  [" fld2(4, 2)
5         "]<----------------------------------------------should be
6         empty\n  [" fld2(3, 11) "]<------------------------should be
7         capped by NF\n  [" fld2() "]\n  [" fld2((OFS=FS="")*($0=$0)+11,
8         23) "]<-------------------FS=\"\", split by chars
9         \n\n|---AFTER-------------------------\n" ($0)
10         "\n|----------------------------"
11  }




12  function fld2(_,__,___,____,_____)
13  {
if (+__==(_=-_<+_ ?+_:_<_) || (___=____="")==__ || !NF) {
return $_
16         } else if (NF<_ || (__=NF<+__?NF:+__)<(_=+_?_:!_)) {
return ___
18         } else if (___==FS || _==!___) {
19            return ___<FS \
? substr("",$!_=$!_ substr("",__=$!(NF=__)))__
20               : substr($(_<_),_,__)
21         }
22         _____=$+(____=___="\37\36\35\32\31\30\27\26\25"\
"\24\23\21\20\17\16\6\5\4\3\2\1")
23         NF=__
24         if ($(!_)~("["(___)"]")) {
25            gsub("..","\\&&",___) + gsub(".",___,____)
27            ___=____
28         }
29         __=(_) substr("",_+=_^=_<_)


30         while(___!="") {
31            if ($(!_)!~(____=substr(___,--_,++_))) {
32               ___=____
33            break }
35            ___=substr(___,_+_^(!_))
36         }
37         return \
substr("",($__=___ $__)==(__=substr($!_,
_+index($!_,___))),_*($!_=_____))(__)
}'

__abc2 __abc3 __abc4 __abc1 __abc5

|---BEFORE-------------------------
1   2  33  4444 555555 <TAB>6666666
|----------------------------


[2 33 4444 555555]
[33]
[]<---------------------------------------------- should be empty
[33 4444 555555 6666666]<------------------------ should be capped by NF
[       1   2  33  4444 555555 <TAB>6666666    ]
[ 2  33  4444 555555 <TAB>66]<------------------- FS="", split by chars


|---AFTER-------------------------
1   2  33  4444 555555 <TAB>6666666
|----------------------------