【问题标题】:Removing whitespace from Strings contained in double quotes bash script从双引号bash脚本中包含的字符串中删除空格
【发布时间】:2012-05-30 13:10:25
【问题描述】:

我一直在使用 sep 来尝试这个,基本上我有一个文本文件,其中包含合理数量的同一行,例如

4444 username "some information" "someotherinformation" "even more information"

我需要用下划线替换引号内的空格,使其看起来像这样

4444 username "some_information" "someotherinformation" "even_more_information"

目前我已经能够分离出引用的信息

sed 's/"\([^"]*\)"/_/g' myfile.txt

关于如何进行的建议?

【问题讨论】:

  • 字段之间的空白有多重要?

标签: string bash unix awk


【解决方案1】:

已编辑

以前的版本会添加不需要的空格。这个版本完全符合 OP 的要求。

这可能是得到你想要的最简单的方法。

awk -F'"' '
  BEGIN {
    OFS="\""
  }
  {
    for (i = 2; i < NF; i += 2) {
      gsub(/[ \t]+/, "_", $i)
    }

    print $0
  }
' file > outputFile

【讨论】:

    【解决方案2】:

    我实际上会在 C 中执行此操作,这使得执行逐个字符的状态机比大多数高级语言更容易。

    #include <stdio.h>
    int main(void)
    {
        int inside_quotes = 0;
        int backslash = 0;
        int c;
        while ((c = getchar()) != EOF) {
            switch (c) {
            case ' ':
                if (inside_quotes)
                    c = '_';
                break;
            case '"':
                if (!backslash)
                    inside_quotes = !inside_quotes;
                break;
            case '\\':
                if (!backslash)
                    backslash = 2;
                break;
            default:
                break;
            }
            if (backslash > 0) backslash--;
            putchar(c);
        }
        return 0;
    }
    

    未经测试甚至编译。特别是反斜杠处理很可能是错误的。

    【讨论】:

      【解决方案3】:
      sed -r ':a; s/^((([^"]*"){2})*[^"]*"[^" ]*) /\1_/;ta'
      4444 username "some_information" "someotherinformation" "even_more_information"
      

      sed ':a; s/^\(\(\([^"]*"\)\{2\}\)*[^"]*"[^" ]*\) /\1_/;ta'
      4444 username "some_information" "someotherinformation" "even_more_information"
      
      • :a - 循环标记为“a”
      • s/// - 执行替换
      • ^( - 将整个搜索字符串锚定在行首
      • (([^"]*"){2})* - 捕获(在第 1 组中)两组零个或多个非引号,后跟一个引号(零次或多次)
      • [^"]*" - 后跟零个或多个非引号,后跟一个引号
      • [^" ]* - 后跟零个或多个非空格或引号的字符
      • ) - 结束锚定序列并寻找需要替换的空间
      • \1 - 用捕获的组和下划线替换匹配的序列
      • ta - 分支(转移执行)到标签 :a 如果已经成功替换(如果没有成功,则继续下一条指令 - 在这种情况下,结束这一行的处理并读取下一条,开始一个新的一轮处理)

      这会在最后一个带引号的字符串中找到第一个空格并替换它。然后下一个,如果有的话,直到引用的字符串完成。对于任何额外的空间,依此类推。

      然后是下一个包含空格的前一个引号字符串......等等。

      这是:a ... ta 循环中每一步的模式空间的样子:

      4444 username "some information" "someotherinformation" "even_more information"
      
      4444 username "some information" "someotherinformation" "even_more_information"
      
      4444 username "some_information" "someotherinformation" "even_more_information"
      

      然后它会多走几次以查找行首的任何匹配项。

      【讨论】:

      • +1 简洁。将([^"]*"){2} 替换为[^"]*"[^"]*" 会降低复杂性,但会牺牲一个角色?
      【解决方案4】:

      这可能对你有用:

      echo '4444 username "some information" "someotherinformation" "even more information"' |
      sed 's/"[^"]*"/\n&/g;:a;s/\(\n"[^"]*\) /\1_/g;ta;s/\n//g'
      4444 username "some_information" "someotherinformation" "even_more_information"
      
      • 为引用的字符串添加一个标记 (\n)。 sed 's/"[^"]*"/\n&amp;/g;
      • _ 替换引用字符串中的所有空格。 :a;s/\(\n"[^"]*\) /\1_/g;ta
      • 删除标记。 s/\n//g

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-09-19
        • 1970-01-01
        • 2021-12-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多