从 stdin 和 stdout 读取和写入文件

从 stdin 和 stdout 读取和写入文件

我需要在 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(如果不存在则不截断并创建它)。

然而,只有ksh93zsh寻求运营商。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

相关内容