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

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

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

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


当前回答

在这里给出的所有其他答案以及在相关问题中给出的各种可能的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 Note that I'm using split() above because it's 3rg arg is a field separator, not just a regexp like the 2nd arg to match(). The difference is that field separators have additional semantics to regexps such as skipping leading and/or trailing blanks when the separator is a single blank char - if you wanted to use a while(match()) loop or any form of *sub() to emulate the above then you'd need to write code to implement those semantics whereas split() already implements them for you.

其他回答

使用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]*用于在输入行中允许前导空格。

更新:

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

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不包含需要regex转义的特殊字符,那么就更容易了:

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

或者如果你更喜欢自定义FS+OFS来处理这一切:

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

这样能行吗?

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

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

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

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

1是第n列,从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

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

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

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

为了避免键入所有这些,我想最好的方法是(参见其他方法)在启动时自动加载用户函数?- 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);
}