【问题标题】:awk - split only by first occurrenceawk - 仅按第一次出现拆分
【发布时间】:2021-09-15 19:02:19
【问题描述】:

我有这样一行:

one:two:three:four:five:six seven:eight

我想用awk 让$1 成为一,$2 成为two:three:four:five:six seven:eight

我知道我可以通过之前的sed 获得它。即用sed 更改: 的第一次出现,然后使用新的分隔符更改awk。

但是用新的分隔符替换分隔符对我没有帮助,因为我不能保证新的分隔符不会出现在文本中。

我想知道是否可以选择让awk 以这种方式行事

比如:

awk -F: '{print $1,$2}'

将打印:

one two:three:four:five:six seven:eight

我还想对$1 和$2 进行一些操作,所以我不想只替换: 的第一次出现。

【问题讨论】:

    标签: bash awk


    【解决方案1】:

    没有任何替换

    echo "one:two:three:four:five" | awk -F: '{ st = index($0,":");print $1 "  " substr($0,st+1)}'
    

    index 命令在整个字符串中查找“:”的第一次出现,所以在这种情况下,变量 st 将被设置为 4。然后我使用 substr 函数从位置开始抓取字符串的所有其余部分st+1,如果没有提供结束编号,它将转到字符串的末尾。输出是

    one  two:three:four:five
    

    如果您想进行进一步处理,您始终可以将字符串设置为变量以进行进一步处理。

    rem = substr($0,st+1)
    

    请注意,这是在 Solaris AWK 上测试过的,但我看不出有任何理由说明这不适用于其他版本。

    【讨论】:

    • 不错的解决方案 +1,我可能会明确设置 $2 和 NF=2 所以 $3 不会仍然包含 three ect awk -F: '{n=index($0,":");$2=substr($0,n+1);NF=2;print $1,$2}'
    • 此解决方案给出 $1=一个 $2=两个 $3=三个等。echo "one:two:three:four:five:six seven:eight" | awk -F: '{ st = index($0,":");print $1 " " substr($0,st+1)}{print $2}' one two:three:four:five:six seven:eight two
    • @Jotne 你读过答案了吗? Adrain 建议将substr 存储在rem 中并使用它。另请参阅我的评论,该评论解决了您的两个问题。
    • 我可能需要睡觉 :)
    • @Jotne - 没问题,很容易做到。 sudo_O - 感谢您的更新和改进。不幸的是,在盘子上给出答案不是我的天性,如果我花时间回答问题,那么我希望他们花一点时间了解解决方案。
    【解决方案2】:

    像这样的?

    echo "one:two:three:four:five:six" | awk '{sub(/:/," ")}1' 
    one two:three:four:five:six
    

    这会将第一个 : 替换为空格。 然后,您可以稍后将其转换为 $1、$2

    echo "one:two:three:four:five:six" | awk '{sub(/:/," ")}1' | awk '{print $1,$2}'
    one two:three:four:five:six
    

    或者在同一个 awk 中,所以即使有替换,你也能以你喜欢的方式得到 $1 和 $2

    echo "one:two:three:four:five:six" | awk '{sub(/:/," ");$1=$1;print $1,$2}'
    one two:three:four:five:six
    

    编辑: 使用不同的分隔符,您可以获得第一个 one 归档 $1 并在 $2 中休息,如下所示:

    echo "one:two:three:four:five:six seven:eight" | awk -F\| '{sub(/:/,"|");$1=$1;print "$1="$1 "\n$2="$2}'
    $1=one
    $2=two:three:four:five:six seven:eight
    

    唯一分隔符

    echo "one:two:three:four:five:six seven:eight" | awk -F"#;#." '{sub(/:/,"#;#.");$1=$1;print "$1="$1 "\n$2="$2}'
    $1=one
    $2=two:three:four:five:six seven:eight
    

    【讨论】:

    • OPs 问题不是关于替换的。它使用分隔符检索字段
    • 确实,我不想对 1 美元和 2 美元进行一些操作。我将编辑我的问题,以便清楚。
    • 更新了我的答案以使其与请求相匹配。
    • 更新问题。我无法替换分隔符。因为我不能保证新的分隔符不会出现在文本中
    • @Jotne 阅读了这个问题,OP 知道如何使用这种方法,他们说他们不想使用这种方法。 #;# 仍然是文件中可能出现的可打印字符串。
    【解决方案3】:

    你能得到的最接近的是 GNU awk 的FPAT:

    $ awk '{print $1}' FPAT='(^[^:]+)|(:.*)' file
    one
    
    $ awk '{print $2}' FPAT='(^[^:]+)|(:.*)' file
    :two:three:four:five:six seven:eight
    

    但$2 将包含前导分隔符,但您可以使用substr 来解决此问题:

    $ awk '{print substr($2,2)}' FPAT='(^[^:]+)|(:.*)' file
    two:three:four:five:six seven:eight
    

    所以把它们放在一起:

    $ awk '{print $1, substr($2,2)}' FPAT='(^[^:]+)|(:.*)' file
    one two:three:four:five:six seven:eight
    

    将substr 的结果存储回$2 将允许在没有前导分隔符的情况下对$2 进行进一步处理:

    $ awk '{$2=substr($2,2); print $1,$2}' FPAT='(^[^:]+)|(:.*)' file
    one two:three:four:five:six seven:eight
    

    应该与mawk 1.3.3 一起使用的解决方案:

    awk '{n=index($0,":");s=$0;$1=substr(s,1,n-1);$2=substr(s,n+1);print $1}' FS='\0'
    one
    
    awk '{n=index($0,":");s=$0;$1=substr(s,1,n-1);$2=substr(s,n+1);print $2}' FS='\0'
    two:three:four five:six:seven
    
    awk '{n=index($0,":");s=$0;$1=substr(s,1,n-1);$2=substr(s,n+1);print $1,$2}' FS='\0'
    one two:three:four five:six:seven
    

    【讨论】:

    • echo "一:二:三:四 五:六:七" | awk '{$2=substr($2,2); print $1,$2}' FPAT='(^[^:]+)|(:.*)' 结果:一:二:三:四 五:六:七:六:七
    • @Udy 你要么没有使用GNU awk,要么你有一个旧版本.. 你在运行哪个版本的awk?
    • mawk 1.3.3 Nov 1996 :)
    • 哇哦!我明确声明您需要 GNU awk 来获得此解决方案。你在什么系统上?你可能已经有GNU awk,如果没有,你应该安装最新的gawk。
    • @Udy 我添加了一个解决方案,它甚至可以与旧的mawk 一起使用。
    【解决方案4】:

    只是把它放在这里作为我想出的解决方案,我想在 : 上拆分前两列,但保持其余行不变。

    内嵌评论。

    echo "a:b:c:d::e" | \
      awk '{
        split($0,f,":");           # split $0 into array of fields `f`
        sub(/^([^:]+:){2}/,"",$0); # remove first two "fields" from `$0` 
        print f[1],f[2],$0         # print first two elements of `f` and edited `$0`
      }'
    

    返回:

    a b c:d::e
    

    在我的输入中,我不必担心前两个字段包含转义的:,如果这是必需的,那么此解决方案不会按预期工作。

    修改为符合原要求:

    echo "a:b:c:d::e" | \
      awk '{
        split($0,f,":");
        sub(/^([^:]+:)/,"",$0);
        print f[1],$0
      }'
    

    返回:

    a b:c:d::e
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-10-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-12-15
      相关资源
      最近更新 更多