我有一个看起来像这样的文件:
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 之外的所有内容”的技巧?
正如 Ben Jackson 所提到的,$1=""
留下了一个空格,因此请使用 for
循环:
awk '{for (i=2; i<=NF; i++) print $i}' filename
因此,如果您的字符串是“一二三”,则输出将是:
二三
如果要将结果放在一行中,可以执行以下操作:
awk '{for (i=2; i<NF; i++) printf $i " "; print $NF}' filename
这会给你:“二三”
分配 $1
有效,但会留下前导空格:awk '{first = $1; $1 = ""; print $0, first; }'
您还可以在 NF
中找到列数并在循环中使用它。
awk {'first = $1; $1=""; print $0'}|sed 's/^ //g'
awk '/>/ {first = $1; $1=""; gsub(/^ /,""); print $0, first}' somefile
将 cut
命令与 -f 2-
(POSIX) 或 --complement
(非 POSIX)一起使用:
$ 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
echo a b c | cut -d' ' -f 2-
是另一种选择
也许是最简洁的方式:
$ 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 去掉第一个字段分隔符。最后的打印是隐式的。
$1=""
(或 $NF=""
,或您正在执行的任何操作)时存在前导或尾随空格问题的绝妙方法。 +1 来自我。
awk '{sub($1 FS,"")}7' YourFile
删除第一个字段和分隔符,并打印结果(7
是非零值,因此打印 $0)。
1
有何不同?我想知道这种模式的用法,并想了解这一点。谢谢!
awk '{ saved = $1; $1 = ""; print substr($0, 2), saved }'
将第一个字段设置为 ""
会在 $0
的开头留下一个 OFS
副本。假设 OFS
只是一个字符(默认情况下,它是一个空格),我们可以用 substr($0, 2)
删除它。然后我们附加 $1
的保存副本。
如果您愿意接受 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 的数组切片
让我们将所有记录移到下一条,并将最后一条设置为第一条:
$ 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) 保存到最后一个字段中。
{}使 awk 执行默认操作的 1 个真条件:{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
gawk 中的字段分隔符(至少)可以是字符串也可以是字符(也可以是正则表达式)。如果您的数据是一致的,那么这将起作用:
awk -F " " '{print $2,$1}' inputfile
这是双引号之间的两个空格。
awk '{ tmp = $1; sub(/^[^ ]+ +/, ""); print $0, tmp }'
选项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
还有一个 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
如果您愿意接受另一个 Perl 解决方案:
perl -ple 's/^(\S+)\s+(.*)/$2 $1/' file
第一次尝试似乎适用于您的特定情况。
awk '{ f = $1; i = $NF; while (i <= 0); gsub(/^[A-Z][A-Z][ ][ ]/,""); print $i, f; }'
还有一种方式...
...这将字段 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}'
使用 cat 命令的另一种简单方法
cat filename | awk '{print $2,$3,$4,$5,$6,$1}' > newfilename
不定期副业成功案例分享
awk '{for(i=2;i<=NF;i++){ printf("%s",( (i>2) ? OFS : "" ) $i) } ; print ;}'
其中:将字段 2 打印到 NF,根据需要添加输出字段分隔符(即,$2 之前除外)。最后一次打印添加最后一个换行符以结束当前行打印。如果您更改 FS/OFS(即,它并不总是“空间”),那一个会起作用