我需要在 bash 中不断读取和写入(虚拟)文件,中间不关闭文件。我正在寻找一个程序,它将打开文件进行读写,从标准输入读取并将其读取的内容写入文件,并从文件读取并将其写入标准输出。有点像netcat
但对于文件。
有问题的文件是 /sys 中的一个虚拟文件(我正在处理的事情中的一个自定义文件),可以向其中写入命令并读取以接收响应 - 其中写入和后续读取必须在同一会话中完成,即在同一个文件描述符上,而不在中间关闭它。
在 C 中很容易做到 - ,, open("file", O_RDWR)
-注意:不需要。write()
read()
seek()
是否有一个标准的 GNU 工具或者我需要编写一个?
答案1
以读+写模式打开文件而不截断的重定向运算符位于<>
所有类似 Bourne 的 shell 中(映射到open(file, O_RDWR|O_CREAT)
(尽管zsh
也会抛出O_NOCTTY
) 或fopen(file, "w+")
):
exec 3<> "$file"
以读+写模式打开$file
文件描述符 3(如果不存在则不截断并创建它)。
然而,只有ksh93
和zsh
有寻求运营商。dd
可以寻找,但不能向后。请注意,除了zsh
变量中可以有 NUL 字节之外,任何 shell 都不能使用。
在zsh
:
zmodload zsh/system
exec 3<> $file
sysread -i 3 -c 2 var # a read() of 2 bytes
sysseek -u 3 0 # seek back to beginning
# or sysseek -u 3 -w current -2 # to seek back 2 bytes
syswrite -o 3 something-else
exec 3<&- # close
在ksh93
:
exec 3<> "$file"
var=$(dd bs=2 count=1 <&3 2>/dev/null; echo .)
var=${var%?}
exec 3<#((0)) # seek to beginning
# or exec 3<#((CUR-2)) # to seek back 2 bytes
print -ru3 something-else
可移植的是,您仍然可以针对您想要的每个偏移量多次打开文件,例如在偏移量 2 处读取和写入 2 个字节(前提是如果不使用 ,它们不是值为 0 的字节zsh
):
var=$(dd bs=2 count=1 skip=1 < "$file"; echo .)
var=${var%?}
printf %s something-else | dd bs=2 seek=1 1<> "$file"
或者:
printf %s something-else | dd bs=2 seek=1 of="$file" conv=notrunc
要读取和写入同一文件,ksh93
还有另外两个有趣的重定向运算符:
tr 01 10 < file >; file
将把输出存储tr
在临时文件中,如果tr
成功,将其重命名为file
(注意该文件是重新创建的,因此可能具有不同的权限和所有权)。
tr -d 0 < file 1<>; file
与标准/Bourne 相同tr -d 0 < file 1<> file
,但如果tr
成功,则在完成写入的file
位置被截断。tr
您可以将其用于产生的输出少于读取输入的过滤命令,或者更准确地说,用于不会读取先前写入的数据的命令。
并且zsh
具有=(...)
流程替换的形式,您可以将其用作:
mv =(tr 01 10 < file) file
(与 的效果和注意事项类似ksh93
)>;
。或者:
cp =(tr 01 10 < file) file
这将保留file
但意味着额外的副本的属性。
现在,如果您需要使用相同的文件描述符在相同的偏移量处读取和写入,并且 zsh 和 ksh93 都不可用,您可以随时恢复为perl
// python
...ruby
perl -e '
open F, "<>", "file" or die "open: $!";
read F, $var, 1;
seek F, 0, 0;
print F "something-else"'
现在,重新阅读问题的更新版本后,您的文件看起来更像是套接字或双向管道,而不是常规的可查找文件。
在这种情况下,这可能只是一个问题:
socat - file:your-file
或者:
(cat >&3 3>&- & cat <&3 3<&-) 3<> your-file
从/向 stdin/stdout 读取数据,将数据馈送到该文件。
请注意,每个人都cat
读取/写入由 shell 打开的文件描述符 3 的自己的副本,但它们共享相同的文件描述符打开文件描述所以它应该是等价的。
答案2
除非你是做翻译的仅有的,您无法同时安全地读取和写入(即使进行翻译,如果程序因错误退出,文件也可能最终“半翻译”)。
听起来您确实想要tee
和的某种组合sponge
。您可以使用sponge
来自更多实用程序吸收标准输入,然后将其写入文件。您可以使用tee
将输入复制到多个输出。
最终结果看起来像这样:
command < filename | tee >(sponge filename)
>()
是一个例子流程替代。在本例中,>(sponge filename)
被替换为 FIFO 的路径,该路径转到 的 stdin sponge
,并tee
进行写入。tee
还独立地将其输入写入标准输出。
这是一个例子。您可以看到输出既写入了 stdout,又写入了文件。
$ echo 'foo bar' > file
$ sed 's/foo/qux/' < file | tee >(sponge file)
qux bar
$ cat file
qux bar