【问题标题】:Print all but the first three columns打印除前三列之外的所有列
【发布时间】:2011-02-07 06:00:05
【问题描述】:

太麻烦了:

awk '{print " "$4" "$5" "$6" "$7" "$8" "$9" "$10" "$11" "$12" "$13}' things

【问题讨论】:

  • 有什么理由不能只使用cut -f3-
  • @hhh 不错。我喜欢总结答案的想法。
  • @Jefromi - 因为 cut 存在行缓冲问题,而 awk 没有:stackoverflow.com/questions/14360640/…
  • @Jefromi - 同样cut{} 操作之前没有正则表达式,然后使用字段分隔符(可变数量的空格?)就更笨了,你必须手动指定它们。我认为 OP 想听到一些不存在的 shift N 命令。最接近的是$1="";$2="";(...);print},但在我的情况下,它会留下一些前导空格(可能是分隔符)。

标签: awk


【解决方案1】:

试试这个:

awk '{ $1=""; $2=""; $3=""; print $0 }'

【讨论】:

  • 这很好,因为它是动态的。您可以在末尾添加列而不重写脚本。
  • 这表明了问题试图解决的确切问题,你只是做相反的事情。那么从第 100 个字段打印呢?注意提到你不处理NF所以你离开领先OFS
【解决方案2】:
echo 1 2 3 4 5| awk '{ for (i=3; i<=NF; i++) print $i }'

【讨论】:

  • 或者让它们在同一行,将 $3 分配给 $1 等,然后将 NF 更改为正确的字段数。 echo 1 2 3 4 5| awk '{ for (i=3; i&lt;=NF; i++) $(i-2)=$i; NF=NF-2; print $0 }'
  • 嗨@larsr。您建议的命令行是唯一正确的答案。所有其他答案都会添加额外的空格(前导或尾随)。请在新答案中发布您的命令行,我将对其进行投票;-)
  • 嗨@sudo_O,我正在和@larsr 谈论他在评论中提出的命令行。我花了大约五分钟才弄清楚 quiproco(误解)。我同意,@Vetsin 答案在字段之间插入新行 (ORS)。为您的倡议而喝彩(我喜欢您的回答)。干杯
  • 我会使用echo 1 2 3 4 5| awk '{ for (i=3; i&lt;=NF; i++) printf $i " "; printf "\n" }'
【解决方案3】:

使用剪切

$ cut -f4-13 file

或者如果你坚持使用 awk 并且 $13 是最后一个字段

$ awk '{$1=$2=$3="";print}' file

其他

$ awk '{for(i=4;i<=13;i++)printf "%s ",$i;printf "\n"}' file

【讨论】:

  • 在上一个示例中使用“NF”可能比使用“13”更好。
  • 2 场景由 OP 决定。如果 13 是最后一个字段,使用 NF 就可以了。如果不是,则使用 13 是合适的。
  • 2nd需要从$0开始删除3份OFS。使用printf "%s ",$i 会更好,因为您不知道$i 是否可能包含%s 等。但这会在最后打印一个额外的空间。
【解决方案4】:
awk '{for(i=1;i<4;i++) $i="";print}' file

【讨论】:

  • 这将留下领先的OFS,因为您不处理NF,即记录中的领先空间。
  • 上帝。为什么不 awk '{$1, $2}'
【解决方案5】:

另一种避免使用打印语句的方法:

 $ awk '{$1=$2=$3=""}sub("^"FS"*","")' file

在 awk 中,当条件为真时打印是默认操作。

【讨论】:

  • 这有@lhf answer 的所有问题.. 它只是更短。
  • 非常好的主意 ;) 比我的回答好! (我去年已经对你的答案投了赞成票)干杯
  • 应该是:awk '{$1=$2=$3=""}sub("^"OFS"+","")' file 就像 OFS 一样,更改 $1、$2 和 $3 内容后剩下的内容。
【解决方案6】:

不添加额外前导或尾随whitespace 的解决方案:

awk '{ for(i=4; i<NF; i++) printf "%s",$i OFS; if(NF) printf "%s",$NF; printf ORS}'

### Example ###
$ echo '1 2 3 4 5 6 7' |
  awk '{for(i=4;i<NF;i++)printf"%s",$i OFS;if(NF)printf"%s",$NF;printf ORS}' |
  tr ' ' '-'
4-5-6-7

Sudo_O 提出了一个优雅的改进,使用三元运算符NF?ORS:OFS

$ echo '1 2 3 4 5 6 7' |
  awk '{ for(i=4; i<=NF; i++) printf "%s",$i (i==NF?ORS:OFS) }' |
  tr ' ' '-'
4-5-6-7

EdMorton 给出了一个保留字段之间原始空格的解决方案:

$ echo '1   2 3 4   5    6 7' |
  awk '{ sub(/([^ ]+ +){3}/,"") }1' |
  tr ' ' '-'
4---5----6-7

BinaryZebra 还提供了两个很棒的解决方案:
(这些解决方案甚至保留原始字符串的尾随空格)

$ echo -e ' 1   2\t \t3     4   5   6 7 \t 8\t ' |
  awk -v n=3 '{ for ( i=1; i<=n; i++) { sub("^["FS"]*[^"FS"]+["FS"]+","",$0);} } 1 ' |
  sed 's/ /./g;s/\t/->/g;s/^/"/;s/$/"/'
"4...5...6.7.->.8->."

$ echo -e ' 1   2\t \t3     4   5   6 7 \t 8\t ' |
  awk -v n=3 '{ print gensub("["FS"]*([^"FS"]+["FS"]+){"n"}","",1); }' |
  sed 's/ /./g;s/\t/->/g;s/^/"/;s/$/"/'
"4...5...6.7.->.8->."

cmets中larsr给出的解决方案几乎是正确的:

$ echo '1 2 3 4 5 6 7' | 
  awk '{for (i=3;i<=NF;i++) $(i-2)=$i; NF=NF-2; print $0}' | tr  ' ' '-'
3-4-5-6-7

这是larsr解决方案的固定和参数化版本:

$ echo '1 2 3 4 5 6 7' | 
  awk '{for(i=n;i<=NF;i++)$(i-(n-1))=$i;NF=NF-(n-1);print $0}' n=4 | tr ' ' '-'
4-5-6-7

2013 年 9 月之前的所有其他答案都很好,但添加了额外的空格:

【讨论】:

  • EdMorton 的回答对我不起作用(bash 4.1.2(1)-release,GNU Awk 3.1.7 或 bash 3.2.25(1)-release,GNU Awk 3.1.5)但是以另一种方式找到hereecho ' This is a test' | awk '{print substr($0, index($0,$3))}'
  • @elysch 不,这通常不起作用,它只是在给定一些特定输入值的情况下才起作用。请参阅我在您的回答下方添加的评论。
  • 嗨@fedorqui。我的回答是第一个。在我原来的答案中,我解释了为什么其他答案不正确(额外的前导或尾随空格)。有些人建议在 cmets 内进行增强。我们已经要求 OP 选择一个更正确的答案,他/她选择了我的。在其他一些贡献者编辑了我的答案以参考那里的答案之后(请参阅历史记录)。你清楚吗?你有什么建议可以提高我的答案的可理解性?干杯;-)
  • 您说的完全正确,对于我的误解,我深表歉意。我快速阅读了答案,并没有注意到您的原始答案(是的,我读得太快了)。为答案本身 +1 使用循环到 NF-1 的好技巧,然后打印最后一个元素以避免额外的空格。再次抱歉! (将在一天左右删除我的评论,以防止未来读者误解)。
  • 我会使用某种标题:,然后是一个水平线,后跟一个大标题“其他答案的比较”。否则,将此比较移至另一个答案,因为显然人们倾向于在“给我我的代码”愿景中更喜欢简短的答案:)
【解决方案7】:

目前几乎所有的答案都添加了前导空格、尾随空格或其他一些分隔符问题。要使用 awk 从分隔符为空格且输出分隔符为单个空格的第四个字段中进行选择,将是:

awk '{for(i=4;i<=NF;i++)printf "%s",$i (i==NF?ORS:OFS)}' file

要对起始字段进行参数化,您可以这样做:

awk '{for(i=n;i<=NF;i++)printf "%s",$i (i==NF?ORS:OFS)}' n=4 file

还有结束字段:

awk '{for(i=n;i<=m=(m>NF?NF:m);i++)printf "%s",$i (i==m?ORS:OFS)}' n=4 m=10 file

【讨论】:

    【解决方案8】:

    执行此操作的正确方法是使用 RE 间隔,因为它可以让您简单地说明要跳过多少字段,并为剩余字段保留字段间距。

    例如考虑到我们在这个问题中讨论的输入格式,跳过前 3 个字段而不影响其余字段之间的间距很简单:

    $ echo '1   2 3 4   5    6' |
    awk '{sub(/([^ ]+ +){3}/,"")}1'
    4   5    6
    

    如果您想容纳前导空格和非空格,但又使用默认 FS,那么它是:

    $ echo '  1   2 3 4   5    6' |
    awk '{sub(/[[:space:]]*([^[:space:]]+[[:space:]]+){3}/,"")}1'
    4   5    6
    

    如果您的 FS 是 RE 不能在字符集中取反,则可以先将其转换为单个字符(如果是单个字符,RS 是理想的,因为 RS 不能出现在字段中,否则请考虑SUBSEP),然后应用 RE 区间替换,然后转换为 OFS。例如如果“.”链分隔字段:

    $ echo '1...2.3.4...5....6' |
    awk -F'[.]+' '{gsub(FS,RS);sub("([^"RS"]+["RS"]+){3}","");gsub(RS,OFS)}1'
    4 5 6
    

    显然,如果 OFS 是单个字符并且它不能出现在输入字段中,您可以将其减少为:

    $ echo '1...2.3.4...5....6' |
    awk -F'[.]+' '{gsub(FS,OFS); sub("([^"OFS"]+["OFS"]+){3}","")}1'
    4 5 6
    

    然后,您会遇到与重新分配字段的所有基于循环的解决方案相同的问题 - FS 被转换为 OFS。如果这是个问题,您需要查看 GNU awks 的 patsplit() 函数。

    【讨论】:

    • 对我不起作用(bash 4.1.2(1)-release、GNU Awk 3.1.7 或 bash 3.2.25(1)-release、GNU Awk 3.1.5)但找到了@ 987654321@另一种方式:echo ' This is a test' | awk '{print substr($0, index($0,$3))}'
    • 不,如果 $1 或 $2 包含 $3 设置的字符串,那将失败。例如,尝试echo ' That is a test' | awk '{print substr($0, index($0,$3))}',您会发现$3 中的a 与$1 中That 中的a 匹配。在像您这样的非常旧版本的 gawk 中,您需要使用标志 --re-interval 启用 RE 间隔。
    • 你是对的,没有注意到。顺便说一句,非常感谢您的评论。很多时候想要使用带有“{}”的正则表达式来指定元素的数量,但从未在 man 中看到“--re-interval”。为你 +1。
    • 1 是一个真条件,因此调用打印当前记录的默认 awk 操作。
    • 我知道它有多规范,但我现在添加了一个答案。
    【解决方案9】:

    这与之前的一些答案相距不远,但确实解决了几个问题:

    cols.sh:

    #!/bin/bash
    awk -v s=$1 '{for(i=s; i<=NF;i++) printf "%-5s", $i; print "" }'
    

    您现在可以使用将作为起始列的参数调用它:

    $ echo "1 2 3 4 5 6 7 8 9 10 11 12 13 14" | ./cols.sh 3 
    3    4    5    6    7    8    9    10   11   12   13   14
    

    或者:

    $ echo "1 2 3 4 5 6 7 8 9 10 11 12 13 14" | ./cols.sh 7 
    7    8    9    10   11   12   13   14
    

    这是 1-indexed;如果您更喜欢零索引,请改用i=s + 1

    此外,如果您想为起始索引结束索引设置参数,请将文件更改为:

    #!/bin/bash
    awk -v s=$1 -v e=$2 '{for(i=s; i<=e;i++) printf "%-5s", $i; print "" }'
    

    例如:

    $ echo "1 2 3 4 5 6 7 8 9 10 11 12 13 14" | ./cols.sh 7 9 
    7    8    9
    

    %-5s 将结果对齐为 5 个字符宽的列;如果这还不够,请增加数字,或者如果您不关心对齐,请使用%s(带空格)。

    【讨论】:

      【解决方案10】:

      基于 AWK printf 的解决方案可避免 % 问题,其独特之处在于如果要打印的列少于 4,它不返回任何内容(无返回字符):

      awk 'NF > 3 { for(i=4; i<NF; i++) printf("%s ", $(i)); print $(i) }'
      

      测试:

      $ x='1 2 3 %s 4 5 6'
      $ echo "$x" | awk 'NF > 3 { for(i=4; i<NF; i++) printf("%s ", $(i)); print $(i) }'
      %s 4 5 6
      $ x='1 2 3'
      $ echo "$x" | awk 'NF > 3 { for(i=4; i<NF; i++) printf("%s ", $(i)); print $(i) }'
      $ x='1 2 3 '
      $ echo "$x" | awk 'NF > 3 { for(i=4; i<NF; i++) printf("%s ", $(i)); print $(i) }'
      $
      

      【讨论】:

        【解决方案11】:

        我不敢相信没有人提供纯壳:

        while read -r a b c d; do echo "$d"; done < file
        

        【讨论】:

        • +1 用于类似的解决方案...但是如果file 很大(>10-30KiB),这可能会出现性能问题。对于大文件,awk 解决方案的性能更好。
        【解决方案12】:
        awk '{$1=$2=$3="";$0=$0;$1=$1}1'
        

        输入

        1 2 3 4 5 6 7
        

        输出

        4 5 6 7
        

        【讨论】:

          【解决方案13】:

          不添加前导或尾随空格的 Perl 解决方案:

          perl -lane 'splice @F,0,3; print join " ",@F' file
          

          perl @F 自动拆分数组从索引 0 开始,而 awk 字段从 $1 开始


          逗号分隔数据的 Perl 解决方案:

          perl -F, -lane 'splice @F,0,3; print join ",",@F' file
          

          Python 解决方案:

          python -c "import sys;[sys.stdout.write(' '.join(line.split()[3:]) + '\n') for line in sys.stdin]" &lt; file

          【讨论】:

            【解决方案14】:

            选项 1 到 3 存在多个空格的问题(但很简单)。 这就是开发选项 4 和 5 的原因,它们可以毫无问题地处理多个空格。 当然,如果选项 4 或 5 与 n=0 一起使用,两者都将保留任何前导空格,因为 n=0 表示不拆分。

            选项 1

            一个简单的剪切解决方案(使用单个分隔符):

            $ echo '1 2 3 4 5 6 7 8' | cut -d' ' -f4-
            4 5 6 7 8
            

            选项 2

            强制 awk 重新计算有时会解决添加前导空格的问题(适用于某些版本的 awk):

            $ echo '1 2 3 4 5 6 7 8' | awk '{ $1=$2=$3="";$0=$0;} NF=NF'
            4 5 6 7 8
            

            选项 3

            打印使用printf 格式化的每个字段将提供更多控制:

            $ echo '    1    2  3     4   5   6 7     8  ' |
              awk -v n=3 '{ for (i=n+1; i<=NF; i++){printf("%s%s",$i,i==NF?RS:OFS);} }'
            4 5 6 7 8
            

            但是,所有先前的答案都将字段之间的所有 FS 更改为 OFS。让我们为此构建几个解决方案。

            选项 4

            使用 sub 删除字段和分隔符的循环更便携,并且不会触发将 FS 更改为 OFS:

            $ echo '    1    2  3     4   5   6 7     8  ' |
            awk -v n=3 '{ for(i=1;i<=n;i++) { sub("^["FS"]*[^"FS"]+["FS"]+","",$0);} } 1 '
            4   5   6 7     8
            

            注意: "^["FS"]*" 用于接受带有前导空格的输入。

            选项 5

            很可能构建一个不添加额外前导或尾随空格的解决方案,并使用来自 GNU awk 的函数 gensub 保留现有空格,如下所示:

            $ echo '    1    2  3     4   5   6 7     8  ' |
            awk -v n=3 '{ print gensub("["FS"]*([^"FS"]+["FS"]+){"n"}","",1); }'
            4   5   6 7     8 
            

            它也可以用于交换给定计数n的字段列表:

            $ echo '    1    2  3     4   5   6 7     8  ' |
              awk -v n=3 '{ a=gensub("["FS"]*([^"FS"]+["FS"]+){"n"}","",1);
                            b=gensub("^(.*)("a")","\\1",1);
                            print "|"a"|","!"b"!";
                           }'
            |4   5   6 7     8  | !    1    2  3     !
            

            当然,在这种情况下,OFS 用于分隔行的两个部分,并且仍然打印字段的尾随空格。

            注意1: ["FS"]* 用于在输入行中允许前导空格。

            【讨论】:

            • 嗨 BZ 你的回答很好。但选项 3 不适用于以空格开头的字符串(例如 " 1 2 3 4 5 6 7 8 ")。选项 4 很好,但使用以空格开头的字符串保留前导空格。你认为这是否可以解决?您可以使用命令echo " 1 2 3 4 5 6 7 8 " | your awk script | sed 's/ /./g;s/\t/-&gt;/g;s/^/"/;s/$/"/' 来验证前导/中间/尾随空格...干杯;)
            • 嗨@olibre。选项 3 因空白而失败是开发选项 4 和 5 的原因。选项 4 仅在输入有它并且 n 设置为 0 (n=0) 时留下前导空格。当没有选择字段时,我相信这是正确的答案(没有什么可以修复 IMO)。干杯。
            • 好的。感谢您提供更多信息 :-) 请改进您的答案,提供这些额外信息 :-) 干杯
            • 完美:-) 可惜你的用户被禁用了:-(
            【解决方案15】:

            Cut 有一个 --complement 标志,可以轻松(快速)删除列。生成的语法与您想要做的类似 - 使解决方案更易于阅读/理解。补充也适用于您想要删除非连续列的情况。

            $ foo='1 2 3 %s 5 6 7'
            $ echo "$foo" | cut --complement -d' ' -f1-3
            %s 5 6 7
            $
            

            【讨论】:

            • 你能解释一下你的答案吗?
            • 上面的编辑有助于理解吗?重点是使用cut的补码标志。该解决方案应该比基于 AWK 或 perl 的解决方案更快、更简洁。此外,可以剪切任意列。
            【解决方案16】:

            对我来说,最紧凑、最合规的解决方案是

            $ a='1   2\t \t3     4   5   6 7 \t 8\t '; 
            $ echo -e "$a" | awk -v n=3 '{while (i<n) {i++; sub($1 FS"*", "")}; print $0}'
            

            如果你有更多行要处理,例如文件 foo.txt,不要忘记将 i 重置为 0:

            $ awk -v n=3 '{i=0; while (i<n) {i++; sub($1 FS"*", "")}; print $0}' foo.txt
            

            感谢您的论坛。

            【讨论】:

              【解决方案17】:

              当我对第一个高度赞成但错误的答案感到恼火时,我发现足以在那里写一个回复,在这里错误的答案被标记为这样,这是我的一点。我不喜欢提出的解决方案,因为我认为没有理由让答案变得如此复杂。

              我有一个日志,其中 5 美元之后的 IP 地址可以是更多文本或没有文本。如果 5 美元之后有任何内容,我需要从 IP 地址到行尾的所有内容。就我而言,这实际上是一个 awk 程序,而不是一个 awk oneliner,所以 awk 必须解决问题。当我尝试使用旧的漂亮且最受好评但完全错误的答案删除前 4 个字段时:

              echo "  7 27.10.16. Thu 11:57:18 37.244.182.218 one two three" | awk '{$1=$2=$3=$4=""; printf "[%s]\n", $0}'
              

              它会吐出错误且无用的响应(我添加了 [] 来演示):

              [    37.244.182.218 one two three]
              

              相反,如果在需要切割点和 awk 之前列的宽度是固定的,那么正确且非常简单的答案是:

              echo "  7 27.10.16. Thu 11:57:18 37.244.182.218 one two three" | awk '{printf "[%s]\n", substr($0,28)}'
              

              产生所需的输出:

              [37.244.182.218 one two three]
              

              【讨论】:

                【解决方案18】:

                我发现了另一种可能性,也许它也有用......

                awk 'BEGIN {OFS=ORS="\t" }; {for(i=1; i&lt;14; i++) print $i " "; print $NF "\n" }' your_file

                注意: 1. 对于表格数据,从 $1 到 $14 列

                【讨论】:

                  【解决方案19】:

                  使用剪切:

                  cut -d <The character between characters> -f <number of first column>,<number of last column> <file name>
                  

                  例如:如果您有 file1 包含:car.is.nice.equal.bmw

                  运行:cut -d . -f1,3 file1 将打印 car.is.nice

                  【讨论】:

                  • 您的解决方案似乎落后了。请查看问题标题打印所有 *but* 前三列
                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 2020-11-27
                  • 2018-11-23
                  • 2013-03-23
                  • 1970-01-01
                  • 2023-03-22
                  • 2020-12-03
                  • 1970-01-01
                  相关资源
                  最近更新 更多