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

发布于 2024-10-03 00:10:57 字数 334 浏览 5 评论 0原文

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

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(17

江心雾 2024-10-10 00:10:57

$1="" 正如 Ben Jackson 提到的那样留下一个空格,因此使用 for 循环:

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

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

两个 < br>

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

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

这将为您提供:“二三”

$1="" leaves a space as Ben Jackson mentioned, so use a for loop:

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

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:

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

This will give you: "two three"

铃予 2024-10-10 00:10:57

分配 $1 有效,但会留下前导空格:awk '{first = $1; $1 =“”;首先打印$0; }'

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


来自 Thyag:要消除前导空格,请将 sed 添加到命令末尾:

awk {'first = $1; $1=""; print $0'}|sed 's/^ //g'

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:

awk {'first = $1; $1=""; print $0'}|sed 's/^ //g'
陈甜 2024-10-10 00:10:57

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

Use the cut command with -f 2- (POSIX) or --complement (not 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
她如夕阳 2024-10-10 00:10:57

也许是最简洁的方法:

$ 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 去掉第一个字段分隔符。最终打印是隐式的。

Maybe the most concise way:

$ 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

Explanation:

$(NF+1)=$1: Generator of a "new" last field.

$1="": Set the original first field to null

sub(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.

绅刃 2024-10-10 00:10:57
awk '{sub($1 FS,"")}7' YourFile

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

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

Remove the first field and separator, and print the result (7 is a non zero value so printing $0).

单挑你×的.吻 2024-10-10 00:10:57
awk '{ saved = $1; $1 = ""; print substr($0, 2), saved }'

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

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

Setting the first field to "" leaves a single copy of OFS at the start of $0. Assuming that OFS is only a single character (by default, it's a single space), we can remove it with substr($0, 2). Then we append the saved copy of $1.

蓝色星空 2024-10-10 00:10:57

如果您对 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

这些命令使用 -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...

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

is a simple solution with an input/output separator of one space, which produces:

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

This next one is slightly more complex

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

and assumes that the input/output separator is two spaces:

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

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

陈年往事 2024-10-10 00:10:57

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

$ 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 true 条件使 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

Let's move all the records to the next one and set the last one as the first:

$ 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

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 make awk perform the default action: {print $0}.

This way, if you happen to have another field separator, the result is also good:

$ 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
憧憬巴黎街头的黎明 2024-10-10 00:10:57

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

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

双引号之间有两个空格。

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:

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

That's two spaces between the double quotes.

初懵 2024-10-10 00:10:57

awk '{ tmp = $1;子(/^[^]+ +/, "");打印 $0, tmp }'

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

随风而去 2024-10-10 00:10:57

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

Option 1

There is a solution that works with some versions of awk:

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

Explanation:

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

Result:

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

However that might fail with older versions of awk.


Option 2

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

That is:

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

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:

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

That line will output:

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

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

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

Even if we change the FS, the OFS and/or add more delimiters, it works.
If the input file is changed to:

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

And the command changes to:

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

The output will be (still preserving delimiters):

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

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:

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

Will output this:

Arab Emirates AE United
& Barbuda AG Antigua
Antilles AN Netherlands
Samoa AS American
and Herzegovina BA Bosnia
Faso BF Burkina
Darussalam BN Brunei
淡写薰衣草的香 2024-10-10 00:10:57

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

There's a sed option too...

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

Explained...

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

More thoroughly explained...

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
一百个冬季 2024-10-10 00:10:57

如果您愿意接受其他 Perl 解决方案:

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

If you're open to another Perl solution:

perl -ple 's/^(\S+)\s+(.*)/$2 $1/' file
酷炫老祖宗 2024-10-10 00:10:57

第一次尝试似乎适合您的特定情况。

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

A first stab at it seems to work for your particular case.

awk '{ f = $1; i = $NF; while (i <= 0); gsub(/^[A-Z][A-Z][ ][ ]/,""); print $i, f; }'
岁吢 2024-10-10 00:10:57

还有另一种方式...

...这将字段 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}'

Yet another way...

...this rejoins the fields 2 thru NF with the FS and outputs one line per line of input

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

I use this with git to see what files have been modified in my working dir:

git diff| \
    grep '\-\-git'| \
    awk '{print$NF}'| \
    awk -F"/" '{for (i=2;i<=NF;i++){printf $i; if (i < NF) {printf FS};}printf RS}'
阳光下的泡沫是彩色的 2024-10-10 00:10:57

我喜欢以下解决方案,因为它会自动使用已为 FS 和 RS 设置的任何内容,而无需对空格、制表符或换行符进行硬编码,甚至无需在输出中引用这些变量。

它也不必使用特殊情况来打印或不打印第一个或最后一个字段之后的字段分隔符。

awk '{ for(i=1;i<NF;i++) $i=$(i+1); NF--; print }'

它将:

  1. 将所有字段向左移动一位
  2. 删除最后一个字段
  3. 使用 awk 的内部连接逻辑以及字段分隔符和记录分隔符打印结果行。

如果要将第一个字段交换到末尾,只需在循环之前添加 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.

awk '{ for(i=1;i<NF;i++) $i=$(i+1); NF--; print }'

It will:

  1. Shift all fields to the left by one place
  2. Remove the last field
  3. Print out the resulting line using awk's internal joining logic with field separators and record separators.

If you want to swap the first field to the end, simply add a first=$1; before the loop and change the NF-- to $NF=first after the loop.

旧伤还要旧人安 2024-10-10 00:10:57

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

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

Another and easy way using cat command

cat filename | awk '{print $2,$3,$4,$5,$6,$1}' > newfilename
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文