【问题标题】:awk to print all columns from the nth to the last with spacesawk 用空格打印从第 n 到最后的所有列
【发布时间】:2015-04-08 21:42:22
【问题描述】:

我有以下输入文件:

a 1  o p
b  2 o p p
c     3 o p p  p

在最后一行中,最后一个p's 之间有一个双空格, 和列有不同的间距

我使用的解决方案来自:Using awk to print all columns from the nth to the last

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

它工作正常,直到它在最后一列达到双倍空格并删除一个空格。

在使用 awk 的同时如何避免这种情况?

【问题讨论】:

  • 您想保留空间吗?如果是这种情况,文件是您所显示的单个字符(或至少是恒定宽度)吗?
  • 使用cut 代替awkcut -d ' ' -f 2-
  • 继续使用 awk 有多重要? (通过 awk -F '[ ]' 能解决问题吗?)
  • @EtanReisner 不能使用剪切,列可能有不同的间距
  • 我不明白?在这种情况下,对cut 重要的唯一间距是第一个列间距。有多少空间会有所不同吗?

标签: bash unix awk


【解决方案1】:

既然你想保留空格,我们就用cut

$ cut -d' ' -f2- file
1 o p
2 o p p
3 o p p  p

或者例如从第 4 列开始:

$ cut -d' ' -f4- file
p
p p
p p  p

只要您要删除的列用一个空格分隔,这将起作用。


如果您要删除的列也包含不同数量的空格,您可以使用 Ed Morton 在Print all but the first three columns 中的漂亮解决方案:

awk '{sub(/[[:space:]]*([^[:space:]]+[[:space:]]+){1}/,"")}1'
                                                   ^
                                        number of cols to remove

测试

$ cat a
a 1 o p
b    2 o p p
c  3 o p p  p
$ awk '{sub(/[[:space:]]*([^[:space:]]+[[:space:]]+){2}/,"")}1' a
o p
o p p
o p p  p

【讨论】:

  • cut 是否支持多个字段分隔符?
  • 不能用cut,必须用awk
  • @JID 你需要事先通过管道传送到tr -s ' '
  • @meso_2600 是您要删除的列,仅用一个空格分隔吗?
  • @fedorqui 必须使用 awk,列有不同的宽度
【解决方案2】:

GNU sed

删除前 n 个字段

sed -r 's/([^ ]+ +){2}//' file

GNU awk 4.0+

awk '{sub("([^"FS"]"FS"){2}","")}1' file

GNU awk

awk --re-interval '{sub("([^"FS"]"FS"){2}","")}1' file

万一 FS 不起作用(Eds 建议)

awk '{sub(/([^ ] ){2}/,"")}1' file

将 2 替换为您要删除的字段数

编辑

另一种方式(不需要重新间隔)

awk '{for(i=0;i<2;i++)sub($1"[[:space:]]*","")}1' file

进一步编辑

按照 EdMorton 的建议,在 sub 中使用字段是不好的,因为它们可能包含元字符,所以这里有一个替代方案(再次!)

awk '{for(i=0;i<2;i++)sub(/[^[:space:]]+[[:space:]]*/,"")}1' file

输出

o p
o p p
o p p  p

【讨论】:

  • 然后在 awk 中执行。 sed 有s,awk 有sub()
  • @EdMorton 不能只使用 sub,列有不同的间距
  • 你错了 - sub 是要使用的命令。我会发布答案。
  • @meso_2600 在发布问题时展示一个涵盖您需要处理的各种情况的问题示例非常重要。编辑您的问题以显示一个真正具有代表性的问题示例,包括您认为可能难以处理/不寻常的情况,并更好地解释您的输入文件中可能包含的内容。否则,我们都只是在猜测和搅动,试图弄清楚你想要什么。
  • @JID 也许吧。您可以尝试awk -v var='.*' '{gsub(/./,"[&amp;]",var); sub(var,...)}' 之类的方法,但这并不适用于所有情况,例如如果var='.*\\' 你会得到一个语法错误。 gsub(/./,"\\\\&amp;",var) 可能会更好,idk.... 我只是避免这样做 - 如果您想替换 RE,请使用对 RE 进行操作的东西,例如 sub(),如果您想替换字符串,请使用字符串函数,例如 index( )+substr()。如果您发现自己试图逃避/禁用所有 RE 元字符,那么显然您不想要 RE!
【解决方案3】:

在 Perl 中,您可以使用 split 和捕获来保留分隔符:

perl -ne '@f = split /( +)/; print @f[ 1 * 2 .. $#f ]'
#                                      ^
#                                      |
#                              column number goes
#                              here (starting from 0)

【讨论】:

  • 必须使用 awk,如原帖所述
  • @meso_2600:祝你好运!
【解决方案4】:

如果您想在第二列开始后保留 所有 个空格,这可以解决问题:

{
    match($0, ($1 "[ \\t*]+"))
    print substr($0, RSTART+RLENGTH)
}

对 match 的调用定位行上第一个“标记”的开始以及第一个标记的长度和它后面的空格。然后,您只需打印 之后的所有内容。

您可以将其概括为以这种方式忽略前 N 个标记:

BEGIN {
    N = 2
}

{
    r = ""
    for (i=1; i<=N; i++) {
        r = (r $i "[ \\t*]+")
    }
    match($0, r)
    print substr($0, RSTART+RLENGTH)
}

将上述脚本应用于您的示例输入会产生:

o p
o p p
o p p  p

【讨论】:

  • 大声笑这是什么网站。这只是awk '{for(i=0;i&lt;2;i++)sub($1"[[:space:]]*","")}1',还有大约十行无用的垃圾
  • 不要这样做。这是一场等待发生的灾难。
  • JID:它的工作原理很像 fedorqui 的脚本,但对于非正则表达式忍者来说更容易阅读。不确定我的解决方案的哪一部分是无用的;我认为这一切都需要得到正确的答案。
  • Ed:您认为我的解决方案有什么问题?
  • 埃德:没关系。我进一步阅读,现在我看到了潜在的问题。如果 $1 包含正则表达式元字符,则 match 不会按预期执行。我试图否决我的答案,但我不能。 :-(
猜你喜欢
  • 2011-02-27
  • 1970-01-01
  • 1970-01-01
  • 2014-03-24
  • 2023-02-04
  • 1970-01-01
  • 2017-04-23
  • 2022-09-23
  • 1970-01-01
相关资源
最近更新 更多