如何使用sed编辑文件描述符

时间:2016-02-01 16:45:25

标签: bash sed sh file-descriptor

我成功地使用了带有sed的文件描述符并在标准输出上给出了结果。提供包含以下内容的文件“file.txt”

$ cat file.txt
foo
Foo

我打开一个文件描述符到file.txt,打开一个子shell,并把这个文件描述符给sed:

$ (sed "/Foo/c\\bar" <&9 ) 9< file.txt
foo
bar

结果是正确的。

现在,如果我想使用sed的-i选项进行更改,我会遇到麻烦。我在读写模式下打开文件描述符,然后将其作为输入文件输入:

$ (sed -i "/Foo/c\\bar" <&9 ) 9<> file.txt
sed: no input file

我不明白为什么缺少输入文件。也许sed在使用-i选项时需要文件名,而不是文件描述符?

我尝试了一种解决方法,当然,它没有按预期工作:

$ (sed "/Foo/c\\bar" <&9 >&9 ) 9<> file.txt
$ cat file.txt
foo
Foo
foo
bar

虽然我期待:

$ cat file.txt
foo
bar

提前感谢您的帮助! Dunatotatos

2 个答案:

答案 0 :(得分:4)

您无法使用sed“就地”编辑任何内容,这是-i被错误命名的一个很好的示例。 gnu sed通过创建新文件,将输出写入其中,然后重命名文件来实现-i。如果你没有给sed提供原始文件名,它就不知道要重命名它。

答案 1 :(得分:2)

sed -i需要一个文件名。您无法传递/dev/stdin(或类似),因为sed会尝试在/dev内创建一个临时文件。

您甚至无法将sed的输出保存到临时文件中,然后再次在文件描述符中写入输出,因为您无法在Bash中回放文件描述符。

您可以做的是从文件描述符中找出原始文件名。您可以使用链接/proc/self/fd/9执行此操作,如下所示:

sed -i "/Foo/c\\bar" "$(readlink /proc/self/fd/9)"

但请注意,原始文件可能已被删除或重命名,在这种情况下,此解决方案将无法使用。此外,此解决方案期望/proc可用,但情况可能并非总是如此。 /dev/fd/9可能是一个很好的替代品。

另一件要注意的事情是sed -i通过用新文件替换文件来工作:在运行sed -i之后,你的fd 9将不会引用新创建的文件。要解决此问题:

name="$(readlink /proc/self/fd/9)"
cp "$name" "$name.tmp"
sed "/Foo/c\\bar" "$name.tmp" > "$name"

这样,你的fd 9仍会在运行sed之前和之后引用同一个文件。您可能希望使用mktemp来创建临时文件,并使用atexit来确保它被删除。