我成功地使用了带有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
答案 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
来确保它被删除。