【问题标题】:bash scripting: Can I get sed to output the original line, then a space, then the modified line?bash 脚本:我可以让 sed 输出原始行,然后是空格,然后是修改后的行吗?
【发布时间】:2019-10-12 21:42:48
【问题描述】:

我是各种形式的 Unix 新手,所以请放轻松! 我有一个 bash 脚本,它将带有任意文件名的 ls 命令通过管道传输到 sed,它将在文件上使用任意替换模式,然后将其通过管道传输到 awk 进行一些处理。问题是,awk 需要知道原始文件名和新文件名。

除了将原始文件名放入 awk 之外,我已经完成了所有工作。例如,假设我的文件是 test.*,而我的替换模式是 's:es:ar;',这会将每次出现的“test”更改为“tart”。出于测试目的,我只是使用 awk 来打印它收到的内容:

ls "$@" | sed "$pattern" | awk '{printf "0: %s\n1: %s\n2: %s\n", $0,$1,$2}'

其中 test.* 在 $@ 中,而模式存储在 $pattern 中。 显然,这并没有让我到达我想去的地方。输出明显是

0: tart.c
1: tart.c
2:

如果我可以让 sed 输出“test.c tart.c”,那么我将有两个用于 awk 的参数。我一直在玩这个模式无济于事,甚至将“test.c”硬编码到替换中。但当然,这只是给了我像“ttest.c art.c”这样的业余结果。 sed 是否可以记住输入,然后将其处理到输出的开头?我什至有正确的想法吗?提前致谢!

【问题讨论】:

  • 最简单的事情是......您可以在 sed 中执行的所有操作都可以在 awk 中执行 - 如果您结合脚本,您的工作最终会更快,更易读。

标签: bash shell awk sed


【解决方案1】:

在重复字段中更改 a b 中第一个 t 的两种方法。

复制(& 重播匹配的部分),更改第一个单词并交换(记住 2 个字符串之间有空格):

echo test.c |  sed -r 's/.*/& &/;s/t/b/;s/([^ ]*) (.*)/\2 \1/'

或者更神奇(将原始值复制到缓冲区,进行更改,将缓冲区中的值作为第一行插入,并用空格替换一行)

echo test.c |  sed 'h;s/t/b/;x;G;s/\n/ /'

【讨论】:

  • @VivekVK 我添加了一些解释。 2年后我认为awk的方案更好,有了awk的一些基础知识,你就会明白代码的作用。 sed 解决方案确实需要解释。
【解决方案2】:
$ echo 'foo' | awk '{old=$0; gsub(/o/,"e"); print old, $0}'
foo fee

【讨论】:

    【解决方案3】:

    使用 Perl 代替 sed:

    echo test.c | perl -lne 'print "$_ ", s/es/ar/r'
    

    -l 从输入中删除换行符并将其添加到每个print 之后。替换的 /r 修饰符返回修改后的字符串,而不是更改变量(需要 Perl 5.14+)。

    旧答案,不适用于s/t/b/2s/.*/replaced/2

    您可以使用s/.*/& &/ 复制该行的内容,然后告诉 sed 它应该只应用第二个替换(这至少在 GNU sed 中有效):

    echo test.c | sed 's/.*/& &/; s/es/ar/2'
    

    【讨论】:

    • 谢谢!我发现一些测试用例没有按预期工作......使用“s/.*/& &/; s/t/b/2”我期望“test.c best.c”但它实际上是“tesb .c 测试.c”。我错了吗?对于“s/.*/replaced/2”,我认为它应该是“test.c 替换”,但它显示“test.c test.c”。我的正则表达式解释可能是错误的......
    • 检查更新。 s/t/b/2把第二个t改成b,所以不能用。
    • 不幸的是,这是一个关于 sed 和 awk 的家庭作业。也许我走错了路。
    猜你喜欢
    • 2019-06-27
    • 2018-07-05
    • 2013-04-05
    • 1970-01-01
    • 1970-01-01
    • 2013-12-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多