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

我有个文件是这样的:

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之外的所有东西”的把戏?

166442 次浏览

分配 $1工作,但它将留下一个领先的空间: awk '{first = $1; $1 = ""; print $0, first; }'

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

第一次尝试似乎对你的特殊情况有效。

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

Gawk 中的字段分隔符(至少)可以是字符串也可以是字符(它也可以是正则表达式)。如果你的数据是一致的,那么这个方法可行:

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

这是双引号之间的两个空格。

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

正如本杰克逊提到的,$1=""留下了一个空格,所以使用 for循环:

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

因此,如果您的字符串是“ one two three”,那么输出将是:


如果希望将结果放在一行中,可以执行以下操作:

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

这会给你: “23”

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

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

-f 2-(POSIX)或 --complement(非 POSIX)使用 cut命令:

$ echo a b c | cut -f 2- -d ' '
b c
$ 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

让我们把所有的记录移到下一个,把最后一个设置为第一个:

$ 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将 Nth 字段值保存到(N-1) th 字段中。
  • $NF=a将第一个值($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

也许最简洁的方法是:

$ 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="": 将原始的第一个字段设置为空

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

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

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

如果您对 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自动拆分修饰符,在本例中拆分为’’(两个空格)

  • -e执行以下 perl 代码

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

的数组切片

选择一

有一种解决方案适用于某些版本的 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,这可能会失败。


选择二

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中删除,然后重新打印。

选择三

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- 间隔选项。原始文件上的这个命令:

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

还有一个 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

还有一种方法。

... 这重新加入字段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}'

如果您打算使用另一种 Perl 解决方案:

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

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

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