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

时间:2010-06-02 21:13:04

标签: linux awk

直到我在第二个字段中有空格时,此行一直有效。

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

有没有办法让awk打印出2美元或更高的价格? ($ 3,$ 4 ..直到我们没有列?)

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

23 个答案:

答案 0 :(得分:420)

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

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

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

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

答案 1 :(得分:90)

simpler answer使用剪切有一个重复的问题:

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

-d指定分隔符(空格)-f指定列(所有以第2个开头)的列表

答案 2 :(得分:76)

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

编辑: 由于“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"}'

答案 3 :(得分:23)

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

我的回答基于the one of VeeArr,但我注意到它在打印第二列(以及其余列)之前以空​​格开头。因为我只有1个声望点,所以我不能评论它,所以这里作为一个新答案:

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

答案 4 :(得分:12)

大多数使用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的值而不是空间(可以更改) 更便携,并且不会触发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]*用于允许输入行中的前导空格。

答案 5 :(得分:12)

我亲自尝试了上面提到的所有答案,但其中大多数都有点复杂或者说不对。从我的角度来看,最简单的方法是:

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

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

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

答案 6 :(得分:6)

这让我非常恼火,我坐下来写了一个cut - 类似的字段规范解析器,用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

为避免输入所有内容,我猜最好的人可以做(另请参阅Automatically load a user function at startup with 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);
}

答案 7 :(得分:5)

这会有用吗?

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

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

答案 8 :(得分:5)

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

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

答案 9 :(得分:4)

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

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

答案 10 :(得分:3)

这是我对所有建议的偏好:

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

答案 11 :(得分:3)

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

lauhub提出了这个正确,简单,快速的解决方案here

答案 12 :(得分:2)

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

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

col#3 col#4

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

COL#3anythingcol#4

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

答案 13 :(得分:2)

Perl解决方案:

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

使用以下命令行选项:

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

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

  • -a autosplit模式 - 将输入行拆分为@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

答案 14 :(得分:0)

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

我知道OP询问awk,但sed方法可以在这里工作(例如从第5页到最后一列打印列):

  • 纯粹的方法

    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和cut: 使用tr选项,-s也可用于squeeze consecutive个字符。

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

答案 15 :(得分:0)

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);

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

middle = fields(3, 5);
函数参数列表中的

b, e, p, i无意义只是一种声明局部变量的awk方式。

答案 16 :(得分:0)

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

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

答案 17 :(得分:0)

如果你不想重新格式化你没有砍掉的那一行,我能想到的最佳解决方案写在我的答案中:

How to print all the columns after a particular number using awk?

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

定义一个函数:

fromField () { 
awk -v m="\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

如果您的文件/流在行中间不包含换行符(您可能使用其他记录分隔符),则可以使用:

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

第一种情况仅在包含稀有十六进制字符编号1

的文件/流中失败

答案 18 :(得分:0)

的Perl:

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

}

答案 19 :(得分:-1)

如果您想要格式化文本,请使用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 |

答案 20 :(得分:-1)

我对此处提供的任何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;'

与来自Chris Koknatperl解决方案相比的优势在于,实际上只有前n个元素从输入字符串中分离出来;字符串的其余部分根本没有分开,因此保持完整。我的例子用空格和标签的混合来证明这一点。

要更改应提取的列数,请将示例中的3替换为n + 1.

答案 21 :(得分:-1)

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

并不差,但自然间距消失了 请将其与此进行比较:

ls -la | cut -d\  -f4-

然后你会看到差异。

到目前为止,基于the answer投票最佳的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
$

答案 22 :(得分:-6)

由于错误的最受欢迎的投票者有340票,我只丢了5分钟的生命!在提出这个问题之前,有没有人尝试过这个答案?显然不是。完全没用。

我有一个日志,其中带有IP地址的5美元后可以是更多文本或没有文本。我需要从IP地址到行尾的所有内容,如果有5美元之后有任何东西。在我的情况下,这实际上是一个awk程序,而不是awk oneliner所以awk必须解决问题。当我尝试使用最受欢迎但完全错误的答案删除前4个字段时:

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]