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


309

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

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

有没有办法让awk打印$ 2或以上的所有内容?($ 3,$ 4 ..直到我们没有更多的列了?)

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


11
grep | awkawk '/!/ { print $2 }'
顺便说一句

3
Unix“剪切”更容易...svn status | grep '\!' | cut -d' ' -f2- > removedProjs
roblogic


@tripleee:很高兴您提到这个-到处看到它令我感到沮丧!
Graham Nicholls

Answers:


489

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

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

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

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

93
陷阱:留下一个关于:(
raphinesse

5
我喜欢务实的方法。尽管无需使用cat,只需将文件名放在awk命令之后。
2013年

45
@raphinesse您可以修复使用awk '{$1=""; print substr($0,2)}' input_filename > output_filename
themiurgo

6
这不适用于非空白定界符,而是将它们替换为空格。
Dejan

3
对于非空白定界符,您可以指定输出字段分隔符(OFS),例如,用逗号表示:awk -F, -vOFS=, '{$1=""; print $0}'您将得到一个初始定界符($1仍包括在内,就像一个空字符串一样)。您可以通过以下方式删除它sedawk -F, -vOFS=, '{$1=""; print $0}' | sed 's/^,//'
cherdt

99

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

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

-d指定分隔符(空格)-f指定列列表(均从2nd开始)


您也可以使用“ -b”指定位置(从第N个字符开始)。
Dakatine 2013年

需要注意的是,尽管它执行的任务与awk版本相同,但存在的行缓冲问题cutawk但没有:stackoverflow.com/questions/14360640/…–
sdaau

24
简洁漂亮,但有一个警告:awk对待多个相邻的空格字符。作为单个分隔符,而cut没有;-尽管这在当前情况下不是问题,但也cut只接受一个文字字符。作为分隔符,而awk允许使用正则表达式。
mklement0 2014年

基于此:stackoverflow.com/a/39217130/8852408,此解决方案可能效率不高。
FcknGioconda '18

85

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

编辑:由于“打印”追加一个换行符,您将要缓冲结果:

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

因此,我尝试了此操作,但认为我丢失了某些内容。.这是我所做的svn status | grep'\!' | gawk'{for(i = 1; i <= $ NF; i ++)print $ i“”;}'> removeProjs
Andy

由于print会添加换行符,因此您需要缓冲结果。看到我的编辑。
VeeArr

1
我更喜欢这个答案,因为它显示了如何遍历字段。
Edward Falk

3
如果要打印使用空格,请更改输出记录分隔符:awk'{ORS =“”; for(i = 2; i <NF; i ++)print $ i}'somefile
Christian Lescuyer 2012年

3
总会有一些空间太多。效果更好:'{for(i=11;i<=NF-1;i++){printf "%s ", $i}; print $NF;}'没有前导或尾随空格。
Marki

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

我的答案基于VeeArr之一,但我注意到它以空格开头,然后才打印第二列(其余部分)。由于我只有1个声誉点,因此我无法对其进行评论,因此这里提供了新的答案:

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


2
太好了,您也删除了out变量前面的$,这也很重要。
亚历克西斯·威尔克2014年

15

大多数使用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

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

$ 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 

它也可以用来交换给定count的一组字段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]*用于在输入行中保留前导空格。


13

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

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

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

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


4
问题,将每个字段打印在不同的行上。
mveroone 2015年

没有什么可以阻止您将其附加在末尾:-)`| tr'\ n'''`
koullislp

3
有些晚但AWK '{对于(I = 5;我<= NF;我++){printf的“%S”,$ I}}'
plitter


7

这让我非常恼火,我坐下来写了一个类似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

为了避免键入所有内容,我想最好的方法是(在启动时自动用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);
}

好像您要使用cut,而不是awk
roblogic

5

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

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

1
很好,尽管您应该+在空格后面加上空格,因为字段可能被awk多个空格分隔(将多个相邻空格视为一个分隔符)。另外,awk将忽略前导空格,因此您应使用来启动正则表达式^[ ]*。使用空格作为分隔符,您甚至可以泛化解决方案。例如,以下代码从第三字段返回所有内容:awk '{sub(/^[ ]*([^ ]+ +){2}/, ""); print $0}'但是,使用任意字段分隔符会变得更加棘手。
mklement0 2014年

5

这行得通吗?

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

但是它在前面留了一些空白。



3

这是我从所有建议中首选的:

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

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

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

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

col#3 col#4

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

col#3anythingcol#4

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


2

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


1

如果您不想重新格式化您不想砍掉的那一部分,那么我能想到的最佳解决方案写在我的答案中:

如何使用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的文件/流中失败


0

如果您使用的是Bash,并且您可以使用与要丢弃的元素一样多的'x',那么它将起作用,并且如果不进行转义,它将忽略多个空格。

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

0

Perl:

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

}

1
这没有回答这个问题,它概括了从第N列到末尾打印的要求。
roaima 2015年

0

awk函数返回的子字符串$0包括从begin到的字段end

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声明局部变量的一种方式。


0

我想将提议的答案扩展到字段可能由多个空格分隔的情况,cut我想是OP不使用的原因。

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

  • 纯sed方法

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

    说明:

    • s/// 用于执行替换的标准方法
    • ^\s* 匹配行首的任何连续空格
    • \S+\s+ 表示一列数据(非空白字符,后跟空白字符)
    • (){4} 表示图案重复了4次。
  • 切割

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

    通过仅用单个选项卡替换连续的空格;

  • tr and cut: tr也可以使用该选项挤压连续字符-s

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

-1

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

-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。


-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 | 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
$

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

-9

由于错误的最投票赞成的投票结果为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]
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.