【问题标题】:How do I run the sed command with input and output as the same file?如何将输入和输出作为同一个文件运行 sed 命令?
【发布时间】:2012-06-05 11:15:29
【问题描述】:

我正在尝试在 shell 脚本中使用 sed 命令,我想删除读取 STARTremoveThisComment 的行和读取 removeThisCommentEND 的行。

当我将它复制到一个新文件时,我可以做到这一点

sed 's/STARTremoveThisComment//' > test

但是如何通过使用相同的文件作为输入和输出来做到这一点呢?

【问题讨论】:

  • sed -e s/some1/some2/g input.file > output.file;mv output.file input.file

标签: sed


【解决方案1】:

sed -i(或扩展版本,--in-place)将自动执行通常由不太高级的实现完成的过程,即将输出发送到临时文件,然后将其重命名为原始文件。

-i 用于就地编辑,您还可以提供备份后缀以保留原始副本:

sed -i.bak fileToChange
sed --in-place=.bak fileToChange

两者都将原始文件保留在fileToChange.bak

请记住,就地编辑可能并非在所有 sed 实现中都可用,但它在 GNU sed,根据您的标签,它应该在所有 Linux 变体上都可用.

如果你使用更原始的实现,你可以使用类似的东西:

cp oldfile oldfile.bak && sed 'whatever' oldfile >newfile && mv newfile oldfile

【讨论】:

  • 工作就像一个魅力! :) 这就是我用的:sed -i 's/STARTremoveThisComment//' test
  • 请注意,--in-place 是一个 gnuism,并且不适用于所有版本的 sed。如果您希望在非 Linux 机器上使用 sed 脚本,则不能使用它。
  • @william,我确实想到了这一点,但并没有太在意,因为这个问题有一个 Linux 标签。我会澄清的。
  • 注意,对于 Mac,您必须为备份文件添加一个参数。所以 sed -i '.bak' 会起作用。如果您不想要文件的两个副本,您可以传递一个空白参数: sed -i ''
【解决方案2】:

您可以使用标志 -i 进行就地编辑,使用 -e 指定正常的脚本表达式:

sed -i -e 's/pattern_to_search/text_to_replace/' file.txt

要删除与特定模式匹配的行,您可以使用更简单的语法。注意 d 标志:

sed -i '/pattern_to_search/d' file.txt

【讨论】:

  • 在我的 Mac 上,这也会创建原始文件的副本,例如 file.txt-e,为什么?
  • 您是否在 -i 和 -e 之间留了一个空格?因为否则它会将 -e 视为备份的后缀。 sed 的帮助显示了 -i 的描述:-i[SUFFIX], --in-place[=SUFFIX] edit files in place (makes backup if SUFFIX supplied) 我刚刚在我的 Linux 机器上再次测试了这个并且工作正常。
【解决方案3】:

您真的不应该为此使用 sed。这个问题似乎经常出现,而且看起来很奇怪,因为一般的解决方案是如此微不足道,人们想知道如何在 sed、python 和 ruby​​ 等中做到这一点似乎很奇怪. 如果你想让过滤器对输入进行操作并覆盖它,请使用以下简单脚本:

#!/bin/sh -e
in=${1?No input file specified}
mv $in ${bak=.$in.bak}
shift
"$@" < $bak > $in

把它放在你的路径中的一个可执行文件名inline中,然后问题一般就解决了。例如:

inline input-file sed -e s/foo/bar/g

现在,如果您想添加逻辑以保留多个备份,或者如果您有一些选项来更改备份命名方案,或者其他什么,您可以在一个地方修复它。使用perl 就地处理文件时,在备份文件上获取 1-up 计数器的命令行选项是什么? ruby 怎么样? gnu-sed 的选项是否不同? awk 是如何处理的? unix 的全部要点是工具只做一件事。备份文件的处理逻辑是第二件事,需要考虑在内。如果您正在实施一个工具,请不要添加逻辑来创建备份文件。告诉您的用户为此使用第二个工具。整合不好。模块化很好。这就是unix方式。

请注意,这个脚本有几个问题。例如,可以更改输入文件的权限/模式。我敢肯定还有无数其他问题。但是,通过将备份逻辑放在包装脚本中,您可以本地化所有这些问题,并且不必担心sed 会覆盖文件并更改模式,而python 会保留文件并且不会更改inode(我编造了这两种情况,关键是并非所有工具都会使用相同的逻辑,而包装脚本会。)

【讨论】:

  • 谢谢,我喜欢这个脚本并根据我的口味调整了它#!/bin/sh -e in=${1?没有指定输入文件} mv "$in" ${bak=in. bak} shift "$@" $in && rm "$bak" || mv "$bak" "$in"
  • 我不同意,如果sed 有这样做的功能,那么我认为没有理由不这样做。它似乎也符合他的要求。
  • @MarkWeiman 问题是“sed”没有那个功能。 sed 的一个特定实现支持该功能,一旦您尝试将脚本移动到使用不同实现的机器上,脚本就会中断。
  • 因此,如果您只打算使用 GNU 实现,请使用它。如果您正在运行多个平台,那么您很可能会意识到差异。据我所知,如果您使用的是 Mac OS X 的 Linux,两者都有 sed-i 标志。我也查了一下,FreeBSD 也有这个功能。
【解决方案4】:

据我所知,输入和输出不能使用相同的文件。虽然一种解决方案是制作一个将其保存到另一个文件的 shell 脚本,但删除旧输入并将输出重命名为输入文件名。

sed -e s/try/this/g input.file > output.file;mv output.file input.file

【讨论】:

  • 至少不应该是 'sed ... & mv ...' 这样 sed 失败不会用空白文件覆盖您的输入吗?
【解决方案5】:

我建议使用sponge

sponge 读取标准输入并将其写入指定文件。 与 shell 重定向不同,海绵在写入之前会吸收所有输入 输出文件。这允许构建读取和读取的管道 写入同一个文件。

cat test | sed 's/STARTremoveThisComment//' | sponge test

【讨论】:

    猜你喜欢
    • 2014-06-13
    • 2015-05-09
    • 1970-01-01
    • 1970-01-01
    • 2017-09-29
    • 1970-01-01
    • 2014-05-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多