使用 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 之外的所有内容”技巧?
I have a file that looks like this:
AE United Arab Emirates
AG Antigua & Barbuda
AN Netherlands Antilles
AS American Samoa
BA Bosnia and Herzegovina
BF Burkina Faso
BN Brunei Darussalam
And I 'd like to invert the order, printing first everything except $1 and then $1:
United Arab Emirates AE
How can I do the "everything except field 1" trick?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(17)
$1=""
正如 Ben Jackson 提到的那样留下一个空格,因此使用for
循环:因此,如果您的字符串是“一二三”,则输出将是:
两个 < br>
三
如果您希望结果在一行中,您可以执行以下操作:
这将为您提供:“二三”
$1=""
leaves a space as Ben Jackson mentioned, so use afor
loop:So if your string was "one two three", the output will be:
two
three
If you want the result in one row, you could do as follows:
This will give you: "two three"
分配
$1
有效,但会留下前导空格:awk '{first = $1; $1 =“”;首先打印$0; }'
您还可以找到
NF
中的列数并在循环中使用它。来自 Thyag:要消除前导空格,请将
sed
添加到命令末尾:Assigning
$1
works but it will leave a leading space:awk '{first = $1; $1 = ""; print $0, first; }'
You can also find the number of columns in
NF
and use that in a loop.From Thyag: To eliminate the leading space, add
sed
to the end of the command:将
cut
命令与-f 2-
(POSIX) 或--complement
(非 POSIX)结合使用:Use the
cut
command with-f 2-
(POSIX) or--complement
(not POSIX):也许是最简洁的方法:
解释:
$(NF+1)=$1
:“新”最后一个字段的生成器。$1=""
:将原来的第一个字段设置为空sub(FS,"")
:在前两个操作之后{$(NF+1)= $1;$1=""}
使用 sub 去掉第一个字段分隔符。最终打印是隐式的。Maybe the most concise way:
Explanation:
$(NF+1)=$1
: Generator of a "new" last field.$1=""
: Set the original first field to nullsub(FS,"")
: After the first two actions{$(NF+1)=$1;$1=""}
get rid of the first field separator by using sub. The final print is implicit.删除第一个字段和分隔符,然后打印结果(
7
是非零值,因此打印 $0)。Remove the first field and separator, and print the result (
7
is a non zero value so printing $0).将第一个字段设置为
""
在$0
的开头留下一个OFS
副本。假设OFS
只是一个字符(默认情况下,它是一个空格),我们可以使用substr($0, 2)
将其删除。然后我们附加保存的$1
副本。Setting the first field to
""
leaves a single copy ofOFS
at the start of$0
. Assuming thatOFS
is only a single character (by default, it's a single space), we can remove it withsubstr($0, 2)
. Then we append the saved copy of$1
.如果您对 Perl 解决方案持开放态度...
是一个简单的解决方案,输入/输出分隔符为一个空格,它会产生:
下一个稍微复杂一些
,并假设输入/输出分隔符是两个空格:
这些命令使用 -line 选项:
-n
循环输入文件的每一行,不自动打印每一行-l
在处理之前删除换行符,然后将它们添加回来-a
自动分割模式 - 将输入行分割到 @F 数组中。默认为按空格分割-F
自动分割修饰符,在此示例中按“ ”(两个空格)分割-e
执行以下 Perl 代码@F 是每行中的单词数组,索引从 0 开始
$#F
是@F
中的单词数
@F[1..$#F]
是元素 1 到最后一个元素的数组切片@F[1..$#F,0]
是元素 1 到最后一个元素加上元素 0 的数组切片If you're open to a Perl solution...
is a simple solution with an input/output separator of one space, which produces:
This next one is slightly more complex
and assumes that the input/output separator is two spaces:
These command-line options are used:
-n
loop around every line of the input file, do not automatically print every line-l
removes newlines before processing, and adds them back in afterwards-a
autosplit mode – split input lines into the @F array. Defaults to splitting on whitespace-F
autosplit modifier, in this example splits on ' ' (two spaces)-e
execute the following perl code@F
is the array of words in each line, indexed starting with 0$#F
is the number of words in@F
@F[1..$#F]
is an array slice of element 1 through the last element@F[1..$#F,0]
is an array slice of element 1 through the last element plus element 0让我们将所有记录移至下一条记录,并将最后一条记录设置为第一条记录:
说明
a=$1
将第一个值保存到临时变量中。for (i=2; i<=NF; i++) $(i-1)=$i
将第 N 个字段值保存到第 (N-1) 个字段中。$NF=a
将第一个值 ($1
) 保存到最后一个字段中。{}1
true 条件使awk
执行默认操作:{print $0}
。这样,如果你碰巧有另一个字段分隔符,结果也不错:
Let's move all the records to the next one and set the last one as the first:
Explanation
a=$1
save the first value into a temporary variable.for (i=2; i<=NF; i++) $(i-1)=$i
save the Nth field value into the (N-1)th field.$NF=a
save the first value ($1
) into the last field.{}1
true condition to makeawk
perform the default action:{print $0}
.This way, if you happen to have another field separator, the result is also good:
gawk 中的字段分隔符(至少)可以是字符串也可以是字符(也可以是正则表达式)。如果您的数据一致,那么这将起作用:
双引号之间有两个空格。
The field separator in gawk (at least) can be a string as well as a character (it can also be a regex). If your data is consistent, then this will work:
That's two spaces between the double quotes.
awk '{ tmp = $1;子(/^[^]+ +/, "");打印 $0, tmp }'
awk '{ tmp = $1; sub(/^[^ ]+ +/, ""); print $0, tmp }'
选项 1
有一个适用于某些版本的 awk 的解决方案:
说明:
结果:
但是对于旧版本的 awk 可能会失败。
选项 2
即:
请注意,需要擦除的是 OFS,而不是 FS 。当字段 $1 被分配时,该行将被重新计算。这会将 FS 的所有运行更改为一次 OFS。
但即使该选项仍然因多个分隔符而失败,如通过更改 OFS 清楚地显示的那样:
该行将输出:
这表明 FS 的运行正在更改为一个 OFS。
避免这种情况的唯一方法是避免字段重新计算。
sub 是一个可以避免重新计算的函数。
第一个字段可以被捕获,然后用 sub 从 $0 中删除,然后重新打印。
选项 3
即使我们更改 FS、OFS 和/或添加更多分隔符,它仍然有效。
如果输入文件更改为:
并且命令更改为:
输出将为(仍保留分隔符):
该命令可以扩展到多个字段,但仅限于现代 awks 且 --re-interval 选项处于活动状态。原始文件上的此命令:
将输出:
Option 1
There is a solution that works with some versions of awk:
Explanation:
Result:
However that might fail with older versions of awk.
Option 2
That is:
Note that what needs to be erased is the OFS, not the FS. The line gets re-calculated when the field $1 is asigned. That changes all runs of FS to one OFS.
But even that option still fails with several delimiters, as is clearly shown by changing the OFS:
That line will output:
That reveals that runs of FS are being changed to one OFS.
The only way to avoid that is to avoid the field re-calculation.
One function that can avoid re-calc is sub.
The first field could be captured, then removed from $0 with sub, and then both re-printed.
Option 3
Even if we change the FS, the OFS and/or add more delimiters, it works.
If the input file is changed to:
And the command changes to:
The output will be (still preserving delimiters):
The command could be expanded to several fields, but only with modern awks and with --re-interval option active. This command on the original file:
Will output this:
还有一个 sed 选项...
解释了...
更彻底地解释了...
There's a sed option too...
Explained...
More thoroughly explained...
如果您愿意接受其他 Perl 解决方案:
If you're open to another Perl solution:
第一次尝试似乎适合您的特定情况。
A first stab at it seems to work for your particular case.
还有另一种方式...
...这将字段 2 到 NF 与 FS 重新连接,并每行输入输出一行
我将其与 git 一起使用来查看我的工作目录中已修改哪些文件:
Yet another way...
...this rejoins the fields 2 thru NF with the FS and outputs one line per line of input
I use this with git to see what files have been modified in my working dir:
我喜欢以下解决方案,因为它会自动使用已为 FS 和 RS 设置的任何内容,而无需对空格、制表符或换行符进行硬编码,甚至无需在输出中引用这些变量。
它也不必使用特殊情况来打印或不打印第一个或最后一个字段之后的字段分隔符。
它将:
如果要将第一个字段交换到末尾,只需在循环之前添加
first=$1;
并将NF--
更改为$NF=first
循环之后。I like the following solution because it will automatically use whatever is already set for FS and RS without having to hard-code spaces or tabs or newlines, and without having to even reference those variables in the output.
It also doesn't have to use special cases for printing or not printing the field separator after the first or last field.
It will:
If you want to swap the first field to the end, simply add a
first=$1;
before the loop and change theNF--
to$NF=first
after the loop.使用 cat 命令的另一种简单方法
Another and easy way using cat command