【问题标题】:Replacing whitespace with single tab unless in double quotes除非在双引号中,否则用单个制表符替换空格
【发布时间】:2015-12-12 04:12:38
【问题描述】:

假设一个多行文件,其中的字符串由一个或多个空格分隔。进一步假设字符串组可以用双引号括起来。

> cat file
foo bar "foobar baz qux"
foo "bar foobar baz" qux
"foo   bar foobar" baz   qux   # multiple whitespaces in this line

如果我希望使用下面列出的 awk 将双引号之外的所有空格替换为单个制表符,我会收到以下信息:

awk '{OFS="\t"; FPAT="([^, ]+)|(\"[^\"]+\")"; $1=$1; print}' file
# foo   bar "foobar baz qux" # In this line, strings inside the quote are separated by tabs
# foo   "bar foobar baz"    qux
# "foo  bar foobar" baz qux

问题似乎仅限于以双引号结尾的行。

编辑 1: 为了更好地可视化手头的问题:

awk '{OFS="\t"; FPAT="([^, ]+)|(\"[^\"]+\")"; $1=$1; print}' file | cat -A
# foo^Ibar^I"foobar^Ibaz^Iqux"$
# foo^I"bar foobar baz"^Iqux$
# "foo   bar foobar"^Ibaz^Iqux$

编辑 2: 除非输入中存在一定数量的非字母字符或非字母字符的组合,否则答案部分中建议的两个命令似乎都可以正常工作。这是一个例子:

> cat file
foo_bar_baz foo foo_bar . Name=foo;product="bar baz qux"
foo_bar_baz foo foo_bar . Name=foo;product="bar baz qux"
foo_bar_baz foo foo_bar . Name=foo;product="bar baz qux"

> awk -v FPAT='"[^"]*"|[^[:blank:]]+' -v OFS='\t' '{$1=$1} 1' file | cat -A
foo_bar_baz^Ifoo^Ifoo_bar^I.^IName=foo;product="bar^Ibaz^Iqux"$
foo_bar_baz^Ifoo^Ifoo_bar^I.^IName=foo;product="bar^Ibaz^Iqux"$
foo_bar_baz^Ifoo^Ifoo_bar^I.^IName=foo;product="bar^Ibaz^Iqux"$

> awk '{$1=$1}1' OFS='\t' FPAT='"[^"]+"|[^ ]+' file | cat -A
foo_bar_baz^Ifoo^Ifoo_bar^I.^IName=foo;product="bar^Ibaz^Iqux"$
foo_bar_baz^Ifoo^Ifoo_bar^I.^IName=foo;product="bar^Ibaz^Iqux"$
foo_bar_baz^Ifoo^Ifoo_bar^I.^IName=foo;product="bar^Ibaz^Iqux"$

编辑 3: EDIT 2 提出的这个问题在这里进一步讨论:Replacing whitespace with single tab unless in double quotes - Part II

【问题讨论】:

    标签: bash awk


    【解决方案1】:

    使用gnu-awk,您可以轻松做到这一点:

    awk -v FPAT='"[^"]*"|[^[:blank:]]+' -v OFS='\t' '{$1=$1} 1' file
    foo bar "foobar baz qux"
    foo "bar foobar baz"    qux
    "foo   bar foobar"  baz qux
    

    【讨论】:

    • 您的命令非常出色。不过,为什么上面的示例会替换第一行双引号之间的空格?
    • 那是因为你设置FPAT 太晚了。它应该像我在BEGIN 块中显示的那样从命令行设置,以便从第 1 行开始生效
    • 所以例如awk 'BEGIN{OFS="\t"; FPAT="([^ ]+)|(\"[^\"]+\")"} {$1=$1} 1' file 也应该适合你。
    • 哦,你已经在cmets中提到了。然后我会删除我的答案...
    • @anubhava 在使用了你的 awk 命令几天后,它似乎只在非常特殊的情况下有效。例如,以下文件未按预期处理:> cat filefoo_bar_baz foo foo_bar . Name=foo;product="bar baz qux"\nfoo_bar_baz foo foo_bar . Name=foo;product="bar baz qux"\nfoo_bar_baz foo foo_bar . Name=foo;product="bar baz qux"
    【解决方案2】:

    我会使用以下命令:

    awk '{$1=$1}1' OFS='\t' FPAT='"[^"]+"|[^ ]+'
    

    我通过FPAT 变量定义了两种可能的字段:

    • 用双引号括起来的字符串"[^"]+"
    • 非空白字符序列[^ ]+

    因为包含的字符串在前,所以优先级更高。

    当管道到cat -A 时,结果可以在 SO 上更好地可视化:

    awk '{$1=$1}1' OFS='\t' FPAT='"[^"]+"|[^ ]+' a.txt | cat -A
    

    输出:

    foo^Ibar^I"foobar baz qux"$
    foo^I"bar foobar baz"^Iqux$
    "foo   bar foobar"^Ibaz^Iqux$
    

    【讨论】:

    • 和用户anubhava的代码一样,下面的文件没有按预期处理,可能是因为存在非字母字符:> cat filefoo_bar_baz foo foo_bar . Name=foo;product="bar baz qux"\nfoo_bar_baz foo foo_bar . Name=foo;product="bar baz qux"\nfoo_bar_baz foo foo_bar . Name=foo;product="bar baz qux"如何你会调整代码来处理非字母字符的存在吗?
    • @MichaelGruenstaeudl 你能把这个例子添加到你的问题中吗?在 cmets 中很难跟踪。请不要编辑现有示例,添加新示例(甚至打开新问题)
    • 新示例已附加到问题中。
    • 好问题。我不知道 atm.. 需要调查一下
    猜你喜欢
    • 2016-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-07
    • 2021-08-08
    相关资源
    最近更新 更多