使用awk打印除第一个字段以外的所有内容


108

我有一个看起来像这样的文件:

AE  United Arab Emirates
AG  Antigua & Barbuda
AN  Netherlands Antilles
AS  American Samoa
BA  Bosnia and Herzegovina
BF  Burkina Faso
BN  Brunei Darussalam

我想反转顺序,先打印除$ 1外的所有内容,然后打印$ 1:

United Arab Emirates AE

如何做“除字段1之外的所有内容”的技巧?


2
嗨@cfisher,它可以在没有循环ang且没有额外空间的情况下完成。
Juan Diego Godoy Robles 2014年

Answers:


91

分配$1作品,但会留下领先的空间:awk '{first = $1; $1 = ""; print $0, first; }'

您还可以找到其中的列数NF并在循环中使用它。


2
对于完全懒惰的人;这是klashxx的代码
Serge Stroobandt,2015年

1
大。用sed摆脱了领导领域: awk {'first = $1; $1=""; print $0'}|sed 's/^ //g'
Thyag

在正常模式下,通过VIM按'Ctrl + V Gd'可以轻松删除空间
Santi

107

$1=""像本杰克逊提到的那样留一个空间,所以使用for循环:

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

因此,如果您的字符串是“一二三”,则输出将是:

2
3

如果要将结果排成一行,可以执行以下操作:

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

这将为您提供:“两个三个”


4
和一个额外的尾随空间
NeronLeVelu

2
更好地使用: awk '{for(i=2;i<=NF;i++){ printf("%s",( (i>2) ? OFS : "" ) $i) } ; print ;}' 将字段2打印到NF,根据需要添加输出字段分隔符(即,在$ 2之前除外)。最后的打印添加最后的换行符以结束当前的行打印。如果您更改FS / OFS(即,它并不总是“ space”),则该方法将起作用
Olivier Dulac

第二个对我来说真的很好。第一个,不是很多。不太确定为什么。它把全文切成小方块。
声音

72

cut命令与--complement选项一起使用:

$ echo a b c | cut -f 1 -d ' '
a
$ echo a b c | cut -f 1,2 -d ' '
a b
$ echo a b c | cut -f 1 -d ' ' --complement
b c

2
虽然没有回答awk特有的问题,但我发现这最有用,因为awk删除了重复的空格,而cut却没有。
Fmstrat 2014年

19
echo a b c | cut -d' ' -f 2- 是另一种选择
Luis

2
尼斯-@Luis解决方案可在Mac上使用,该软件不支持--complement
metadaddy

21

也许是最简洁的方法:

$ awk '{$(NF+1)=$1;$1=""}sub(FS,"")' infile
United Arab Emirates AE
Antigua & Barbuda AG
Netherlands Antilles AN
American Samoa AS
Bosnia and Herzegovina BA
Burkina Faso BF
Brunei Darussalam BN

说明:

$(NF+1)=$1:生成“新”最后字段。

$1="":将原始的第一个字段设置为null

sub(FS,""):在执行前两个操作之后{$(NF+1)=$1;$1=""},使用sub摆脱了第一个字段分隔符。最终打印是隐式的。


13
awk '{sub($1 FS,"")}7' YourFile

删除第一个字段和分隔符,并打印结果(这7是一个非零值,所以打印$ 0)。


最佳答案!已投票。与仅使用它有何不同1?我想知道这种模式的用法,并想了解这一点。谢谢!
Abhijeet Rastogi

10
awk '{ saved = $1; $1 = ""; print substr($0, 2), saved }'

将第一个字段设置""OFS在的开头保留的单个副本$0。假设这OFS只是一个字符(默认情况下为单个空格),我们可以使用删除它substr($0, 2)。然后,我们附加的已保存副本$1


6

如果您愿意接受Perl解决方案...

perl -lane 'print join " ",@F[1..$#F,0]' file

是一个输入/输出分隔符为一个空格的简单解决方案,它产生:

United Arab Emirates AE
Antigua & Barbuda AG
Netherlands Antilles AN
American Samoa AS
Bosnia and Herzegovina BA
Burkina Faso BF
Brunei Darussalam BN

下一个稍微复杂一点

perl -F`  ` -lane 'print join "  ",@F[1..$#F,0]' file

并假定输入/输出分隔符为两个空格:

United Arab Emirates  AE
Antigua & Barbuda  AG
Netherlands Antilles  AN
American Samoa  AS
Bosnia and Herzegovina  BA
Burkina Faso  BF
Brunei Darussalam  BN

这些命令行选项用于:

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

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

  • -a自动拆分模式–将输入行拆分为@F数组。默认为在空白处分割

  • -F autosplit修饰符,在本例中为''(两个空格)

  • -e 执行以下Perl代码

@F是每行中的单词数组,索引从0开始
$#F是in的单词数@F
@F[1..$#F]是元素1到最后一个元素
@F[1..$#F,0]的数组切片是元素1到最后一个元素加元素0的数组切片


1
我运行它并在末尾有一个额外的数字,所以我使用了这个版本:perl -lane'shift @F; 打印加入“”,@ F”
汉斯便便

2

gawk中的字段分隔符(至少)可以是字符串,也可以是字符(也可以是正则表达式)。如果您的数据是一致的,那么它将起作用:

awk -F "  " '{print $2,$1}' inputfile

在双引号之间是两个空格。


对于当前情况的最佳答案,但是,从技术上讲,这没有回答如何打印除第一个字段以外的所有内容的问题。
Dan Molding

@DanMoulding:只要文件在使用两个空格分隔国家代码方面保持一致,并且没有其他两个空格一起出现,我的回答是 确实可以解决这个问题。
暂停,直到另行通知。

2
遇到此问题的人之所以来到这里,是因为他们想知道如何打印除第一个字段以外的所有内容(请参阅问题标题)。那就是我降落在这里的方式。您的答案显示了如何打印第一个字段,然后打印第二个字段。虽然这可能是OP特定情况的最佳解决方案,但它不能解决除了第一个字段以外如何打印所有内容的一般问题。
Dan Molding

2

awk '{ tmp = $1; sub(/^[^ ]+ +/, ""); print $0, tmp }'


2

让我们将所有记录移至下一个,并将最后一个设置为第一个:

$ awk '{a=$1; for (i=2; i<=NF; i++) $(i-1)=$i; $NF=a}1' file
United Arab Emirates AE
Antigua & Barbuda AG
Netherlands Antilles AN
American Samoa AS
Bosnia and Herzegovina BA
Burkina Faso BF
Brunei Darussalam BN

说明

  • a=$1 将第一个值保存到一个临时变量中。
  • for (i=2; i<=NF; i++) $(i-1)=$i 将第N个字段值保存到第(N-1)个字段中。
  • $NF=a将第一个值($1)保存到最后一个字段中。
  • {}1使awk执行默认操作的真实条件:{print $0}

这样,如果碰巧有另一个字段分隔符,结果也很好:

$ cat c
AE-United-Arab-Emirates
AG-Antigua-&-Barbuda
AN-Netherlands-Antilles
AS-American-Samoa
BA-Bosnia-and-Herzegovina
BF-Burkina-Faso
BN-Brunei-Darussalam

$ awk 'BEGIN{OFS=FS="-"}{a=$1; for (i=2; i<=NF; i++) $(i-1)=$i; $NF=a}1' c
United-Arab-Emirates-AE
Antigua-&-Barbuda-AG
Netherlands-Antilles-AN
American-Samoa-AS
Bosnia-and-Herzegovina-BA
Burkina-Faso-BF
Brunei-Darussalam-BN

1

乍一看似乎适合您的特定情况。

awk '{ f = $1; i = $NF; while (i <= 0); gsub(/^[A-Z][A-Z][ ][ ]/,""); print $i, f; }'

1

选项1

有一种解决方案可与某些版本的awk配合使用:

awk '{ $(NF+1)=$1;$1="";$0=$0;} NF=NF ' infile.txt

说明:

       $(NF+1)=$1                          # add a new field equal to field 1.
                  $1=""                    # erase the contents of field 1.
                        $0=$0;} NF=NF      # force a re-calc of fields.
                                           # and use NF to promote a print.

结果:

United Arab Emirates AE
Antigua & Barbuda AG
Netherlands Antilles AN
American Samoa AS
Bosnia and Herzegovina BA
Burkina Faso BF
Brunei Darussalam BN

但是,使用旧版本的awk可能会失败。


选项2

awk '{ $(NF+1)=$1;$1="";sub(OFS,"");}1' infile.txt

那是:

awk '{                                      # call awk.
       $(NF+1)=$1;                          # Add one trailing field.
                  $1="";                    # Erase first field.
                        sub(OFS,"");        # remove leading OFS.
                                    }1'     # print the line.

请注意,需要擦除的是OFS,而不是FS。分配字段$ 1时,将重新计算该行。这将所有FS的运行更改为一个OFS。


但是即使通过几个定界符,该选项仍然失败,如更改OFS所示:

awk -v OFS=';' '{ $(NF+1)=$1;$1="";sub(OFS,"");}1' infile.txt

该行将输出:

United;Arab;Emirates;AE
Antigua;&;Barbuda;AG
Netherlands;Antilles;AN
American;Samoa;AS
Bosnia;and;Herzegovina;BA
Burkina;Faso;BF
Brunei;Darussalam;BN

这表明FS的运行已更改为一个OFS。
避免这种情况的唯一方法是避免重新计算字段。
sub是可以避免重新计算的一项功能。
可以捕获第一个字段,然后使用sub将其从$ 0中删除,然后重新打印。

选项3

awk '{ a=$1;sub("[^"FS"]+["FS"]+",""); print $0, a;}' infile.txt
       a=$1                                   # capture first field.
       sub( "                                 # replace: 
             [^"FS"]+                         # A run of non-FS
                     ["FS"]+                  # followed by a run of FS.
                            " , ""            # for nothing.
                                  )           # Default to $0 (the whole line.
       print $0, a                   # Print in reverse order, with OFS.


United Arab Emirates AE
Antigua & Barbuda AG
Netherlands Antilles AN
American Samoa AS
Bosnia and Herzegovina BA
Burkina Faso BF
Brunei Darussalam BN

即使我们更改FS,OFS和/或添加更多定界符,它也可以工作。
如果输入文件更改为:

AE..United....Arab....Emirates
AG..Antigua....&...Barbuda
AN..Netherlands...Antilles
AS..American...Samoa
BA..Bosnia...and...Herzegovina
BF..Burkina...Faso
BN..Brunei...Darussalam

然后命令更改为:

awk -vFS='.' -vOFS=';' '{a=$1;sub("[^"FS"]+["FS"]+",""); print $0,a;}' infile.txt

输出将是(仍保留定界符):

United....Arab....Emirates;AE
Antigua....&...Barbuda;AG
Netherlands...Antilles;AN
American...Samoa;AS
Bosnia...and...Herzegovina;BA
Burkina...Faso;BF
Brunei...Darussalam;BN

该命令可以扩展到多个字段,但只能在现代awks且--re-interval选项处于活动状态时使用。此命令在原始文件上:

awk -vn=2 '{a=$1;b=$2;sub("([^"FS"]+["FS"]+){"n"}","");print $0,a,b;}' infile.txt

将输出以下内容:

Arab Emirates AE United
& Barbuda AG Antigua
Antilles AN Netherlands
Samoa AS American
and Herzegovina BA Bosnia
Faso BF Burkina
Darussalam BN Brunei

1

如果您愿意使用其他Perl解决方案:

perl -ple 's/^(\S+)\s+(.*)/$2 $1/' file

0

还有一个sed选项...

 sed 's/\([^ ]*\)  \(.*\)/\2 \1/' inputfile.txt

解释...

Swap
\([^ ]*\) = Match anything until we reach a space, store in $1
\(.*\)    = Match everything else, store in $2
With
\2        = Retrieve $2
\1        = Retrieve $1

更详尽的解释...

s    = Swap
/    = Beginning of source pattern
\(   = start storing this value
[^ ] = text not matching the space character
*    = 0 or more of the previous pattern
\)   = stop storing this value
\(   = start storing this value
.    = any character
*    = 0 or more of the previous pattern
\)   = stop storing this value
/    = End of source pattern, beginning of replacement
\2   = Retrieve the 2nd stored value
\1   = Retrieve the 1st stored value
/    = end of replacement

0

还有另一种方式

...这会将字段2至NF与FS重新连接在一起,并且每行输入输出一行

awk '{for (i=2;i<=NF;i++){printf $i; if (i < NF) {printf FS};}printf RS}'

我将其与git一起使用,以查看工作目录中已修改了哪些文件:

git diff| \
    grep '\-\-git'| \
    awk '{print$NF}'| \
    awk -F"/" '{for (i=2;i<=NF;i++){printf $i; if (i < NF) {printf FS};}printf RS}'

-3

使用cat命令的另一种简单方法

cat filename | awk '{print $2,$3,$4,$5,$6,$1}' > newfilename

我投反对票,因为这不是动态方法。与此相关,您需要知道参数的数量并假设您的数据是一致的。数据几乎永远不会保持一致,因此您的方法大多数时候都必须考虑到这一点。
xh3b4sd
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.