web-dev-qa-db-ja.com

stdinおよびstdoutからの読み取りおよびファイルへの書き込み

Bashで(仮想)ファイルの読み取りと書き込みを継続的に行う必要があります間にファイルを閉じずに。ファイルを読み取りと書き込み用に開き、stdinから読み取り、読み取ったものをファイルに書き込み、ファイルから読み取り、stdoutに書き込むプログラムを探しています。 netcatに少し似ていますが、ファイル用です。

問題のファイルは/ sys内の仮想ファイル(私が取り組んでいるもののカスタムのもの)であり、コマンドを書き込んで読み取り、応答を受信します。書き込みと後続の読み取りは同じセッションで実行する必要があります。つまり、同じファイル記述子上で、間に閉じずに。

Cで簡単に実行できます--open("file", O_RDWR)write()read()-注:seek()の必要はありません。

標準のGNUツールはありますか、それとも作成する必要がありますか?

2
Mark Smith

切り捨てなしで読み取り+書き込みモードでファイルを開くためのリダイレクト演算子は、すべてのボーンのようなシェルで<>です(open(file, O_RDWR|O_CREAT)にマップされます(ただし、zshO_NOCTTYをスローします)またはfopen(file, "w+")):

exec 3<> "$file"

ファイル記述子3の$fileを読み取り+書き込みモードで開きます(切り捨てたり、存在しなかった場合は作成したりしません)。

ただし、ksh93zshのみがseeking演算子を持っています。 ddはシークできますが、逆方向にはシークできません。また、zsh以外のシェルは変数にNULバイトを含めることができないことに注意してください。

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バイトを読み書きするように、必要なオフセットごとにファイルを数回開くことができます(zshを使用していない場合は値0のバイトではない場合)。

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には他に2つの興味深いリダイレクト演算子があります。

tr 01 10 < file >; file

trの出力を一時ファイルに保存し、trが成功した場合は、名前をfileに変更します(ファイルは新しく作成されるため、権限と所有権が異なる可能性があることに注意してください) )。

tr -d 0 < file 1<>; file

標準/ボーン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は、シェルによって開かれたファイル記述子3の独自のコピーに対して読み取り/書き込みを行いますが、同じファイル記述を開くを共有するため、同等である必要があります。

3

翻訳のみを行わない限り、安全に同時に読み取りと書き込みを行うことはできません(翻訳を行っても、ファイルが終了する可能性があります)プログラムがエラーで終了した場合は、「半分翻訳」されます)。

teespongeの組み合わせが本当に必要なようです。 moreutils からspongeを使用して、標準入力を吸収し、それをファイルに書き出すことができます。 teeを使用して、入力を複数の出力に複製できます。

最終結果は次のようになります。

_command < filename | tee >(sponge filename)
_

>()プロセス置換 の例です。この場合、>(sponge filename)は、spongeが書き込むteeの標準入力に向かうFIFOへのパスに置き換えられます。 .teeは、入力をstdoutに個別に書き込みます。

これが例です。出力がstdoutに書き込まれ、ファイルに書き込まれていることがわかります。

_$ echo 'foo bar' > file
$ sed 's/foo/qux/' < file | tee >(sponge file)
qux bar
$ cat file
qux bar
_
0
Chris Down