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

现在我有这条线,它的工作,直到我有第二个领域的空白。

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

有没有办法让awk打印所有$ 2或更多的东西? ($ 3,$ 4,直到我们再没有列了?)

我想我应该补充一点,我在Cygwin的Windows环境下这样做。

将打印所有,但非常第一列:

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

将打印除第一列之外的所有列:

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

有一个重复的问题,用简单的答案使用cut:

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

-d指定了分隔符(空格) ,- -f指定列的列表(全部以第二个开头)

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

编辑:由于“打印”附加一个换行符,你会想缓冲结果:

 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 '{out=$2; for(i=3;i<=NF;i++){out=out" "$i}; print out}' 

我的答案是基于VeeArr的 ,但是我注意到它在打印第二列(和其他列)之前以空​​格开始。 由于我只有1个声望点,我不能评论它,所以这里作为一个新的答案:

作为第二列开始“out”,然后添加所有其他列(如果它们存在)。 只要有第二栏,这个就行了。

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

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

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

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

这让我非常恼火,我坐下来写了一个类似于GNU Awk 3.1.7的cut式字段规范parsing器。

首先,创build一个名为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 

为了避免input所有这些,我想最好的办法就是做(另外请参阅在启动时用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 eg `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: "ab" # 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); } 

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

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

这会工作吗?

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

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

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

这个使用awk打印除最后一个字段外的所有内容

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

选项1

一个简单的解决scheme(只适用于单个分隔符):

 command | cut -d' ' -f3- 

选项2

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

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

选项3

printf格式打印每个字段将会给予更多的控制:

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

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

选项4(推荐)

与子的循环删除前面的字段和分隔符。
并使用FS的值而不是空间(可以改变)。
更便携,并且不会触发将FS更改为OFS: 注意: ^[FS]*将接受具有前导空格的input。

 $ 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

构build一个不会增加额外(前导或尾随)空白的解决scheme,并使用GNU awk中的函数gensub保留现有的空白是完全可能的,如下所示:

 $ echo ' 1 2 3 4 5 6 7 8 ' | awk -vn=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 -vn=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]*用于允许input行中的前导空格。

这是我从所有的build议中select的:

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

 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"}' 

如果您需要使用任意分度符打印的特定列:

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

第3栏第4栏

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

山口#3anythingcol#4

所以,如果你在列中有空格,它将是两列,但你可以用任何分隔符连接它,或者没有它。

Perl解决scheme:

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

这些命令行选项被使用:

  • -n循环input文件的每一行,不要自动打印每一行

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

  • -a自动分割模式 – 将input行分割成@F数组。 默认分割为空格

  • -e执行Perl代码

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

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


Python解决scheme:

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

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

lauhub 在这里提出了这个正确,简单和快速的解决scheme

如果你想要格式化的文本,链接你的命令与回声,并使用$ 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 | 

如果你使用的是Bash,那么这将会起作用,你可以使用与你想要丢弃的元素一样多的“x”,并且如果它们没有被转义,它将忽略多个空格。

 while read xb; do echo "$b"; done < filename 

如果你不想重新格式化你没有删除的部分,我能想到的最好的解决scheme是写在我的答案中:

如何使用awk打印特定数字后的所有列?

它打印给定字段号N之前的内容,并打印该行的所有其余部分,包括字段号N并保持原始间距(不重新格式化)。 如果该字段的string也出现在该行的其他位置,则不起作用。

定义一个函数:

 fromField () { awk -vm="\x01" -v N="$1" '{$N=m$N; print substr($0,index($0,m)+1)}' } 

像这样使用它:

 $ echo " bat bi iru lau bost " | fromField 3 iru lau bost $ echo " bat bi iru lau bost " | fromField 2 bi iru lau bost 

输出保持一切,包括尾随空格

在你特别的情况下:

 svn status | grep '\!' | fromField 2 > removedProjs 

如果您的文件/stream在行中间不包含换行符(可以使用不同的logging分隔符),则可以使用:

 awk -vm="\x0a" -v N="3" '{$N=m$N ;print substr($0, index($0,m)+1)}' 

第一种情况只会在包含罕见的hex字符数1的文件/stream中失败

Perl的:

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

这个awk函数返回$0子string,包括从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); 

要获得包含字段3到5的$0部分:

 middle = fields(3, 5); 

b, e, p, i在函数参数列表中只是一个声明局部variables的awk方法。

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

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

其中1是从0开始计数的第n列。


鉴于这个文件的内容( 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解决scheme并不满意,因为我想提取前几列然后打印其余的,所以我转而使用perl 。 以下代码提取前两列,并按原样显示其余的内容:

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

与Chris Koknat的perl解决scheme相比,它的优势在于,只有前n个元素从inputstring中分离出来; string的其余部分根本不分裂,因此保持完整。 我的例子演示了空间和制表符的混合。

要更改应提取的列的数量,请将示例中的3replace为n + 1。

 ls -la | awk '{o=$1" "$3; for (i=5; i<=NF; i++) o=o" "$i; print o }' 

从这个答案不坏,但自然间距已经消失。
请把它和这个比较一下:

 ls -la | cut -d\ -f4- 

然后你会看到差异。

即使ls -la | awk '{$1=$2=""; print}' ls -la | awk '{$1=$2=""; print}' ls -la | awk '{$1=$2=""; print}'这是基于迄今为止投票最好的答案是不保存格式。

因此,我将使用以下内容,并且还允许在开始时显式select列:

 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 $