Чтение и запись в файл из stdin и stdout

Мне нужно постоянно читать и писать в (виртуальный) файл в bash, не закрывая файл между ними . Я ищу программу, которая откроет файл для чтения и записи, прочитает из stdin и напишет, что он читает в файле И читает из файла и записывает его в stdout. Немного похоже на netcat но на файлы.

Этот файл является виртуальным файлом в / sys (пользовательская вещь в чем-то, над чем я работаю), к которой вы пишете команду, и читает, чтобы получить ответ – где запись и последующее чтение должны выполняться в том же сеансе, т.е. в том же дескрипторе файла, не закрывая его между ними.

Легко сделать в C – open("file", O_RDWR) , write() , read() – примечание: нет необходимости seek() .

Есть ли стандартный инструмент GNU или мне нужно написать его?

Оператор перенаправления для открытия файла в режиме чтения и записи без усечения – это <> во всех оболочках типа Bourne (которые отображаются для open(file, O_RDWR|O_CREAT) (хотя zsh также выбрасывает O_NOCTTY ) или fopen(file, "w+") ):

 exec 3<> "$file" 

открывает $file в дескрипторе файла 3 в режиме чтения + записи (без его усечения и создания, если он не существует).

Однако только ksh93 и zsh ищут операторов. 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 (при условии, что они не являются байтами со значением 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 's >; ). Или:

  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, открытого оболочкой, но они имеют одинаковое открытое описание файла, поэтому оно должно быть эквивалентным.

Если вы не выполняете только перевод, вы не можете читать и писать в одно и то же время безопасно (и даже при переводе возможно, что файл может оказаться «переведенным наполовину», если программа выходит с ошибкой).

Похоже, вам действительно нужна комбинация tee и sponge . Вы можете использовать sponge из moreutils для впитывания стандартного ввода, а затем записать его в файл. Вы можете использовать tee для дублирования ввода для нескольких выходов.

Конечный результат будет выглядеть примерно так:

 command < filename | tee >(sponge filename) 

>() является примером замещения процесса . В этом случае >(sponge filename) заменяется на путь к FIFO, который идет на stdin sponge , на который пишет tee . tee также записывает свой вход в stdout самостоятельно.

Вот пример. Вы можете видеть, что вывод записывается в stdout и записывается в файл.

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