Lê e escreve no arquivo de stdin e stdout

1

Eu preciso ler e escrever continuamente em um arquivo (virtual) no bash, sem fechar o arquivo entre . Estou à procura de um programa que irá abrir o arquivo para ler e escrever, ler a partir de stdin e escrever o que lê no arquivo e ler a partir do arquivo e escrevê-lo para stdout. Um pouco como netcat , mas para arquivos.

O arquivo em questão é um arquivo virtual em / sys (um item personalizado em algo em que estou trabalhando) ao qual um escreve um comando e lê para receber a resposta - onde a gravação e a leitura subseqüente devem ser feitas no arquivo. mesma sessão, ou seja, no mesmo descritor de arquivo sem fechá-lo no meio.

Fácil de fazer em C - open("file", O_RDWR) , write() , read() - nota: não é necessário seek() .

Existe uma ferramenta GNU padrão ou eu preciso escrever uma?

    
por Mark Smith 20.03.2016 / 21:19

2 respostas

2

O operador de redirecionamento para abrir um arquivo no modo de leitura + gravação sem truncamento é <> em todos os shells semelhantes a Bourne (que mapeia para open(file, O_RDWR|O_CREAT) (embora zsh também ative O_NOCTTY ) ou fopen(file, "w+") ):

exec 3<> "$file"

abre o $file no descritor de arquivo 3 no modo leitura + gravação (sem truncá-lo e criá-lo se ele não existir).

No entanto, apenas ksh93 e zsh têm procurando operadores. dd pode procurar, mas não para trás. E note que nenhum shell, exceto zsh , pode ter bytes NUL em suas variáveis.

Em 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

Em 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

Portualmente, você ainda pode abrir o arquivo várias vezes, para cada deslocamento desejado, como aqui para ler e escrever 2 bytes no deslocamento 2 (desde que não sejam bytes com valor 0 se não estiver usando zsh ):

var=$(dd bs=2 count=1 skip=1 < "$file"; echo .)
var=${var%?}
printf %s something-else | dd bs=2 seek=1 1<> "$file"

Ou:

printf %s something-else | dd bs=2 seek=1 of="$file" conv=notrunc

Para ler e gravar no mesmo arquivo, ksh93 tem dois outros operadores de redirecionamento interessantes:

tr 01 10 < file >; file

Armazenaria a saída de tr em um arquivo temporário e se tr for bem-sucedida, renomeie para file (cuidado com o fato de o arquivo ser criado novamente, portanto com permissões e propriedade possivelmente diferentes).

tr -d 0 < file 1<>; file

O mesmo que o padrão / Bourne tr -d 0 < file 1<> file , exceto que, se tr tiver êxito, file será truncado onde tr terminou de gravar. Você pode usar isso para comandos de filtro que produzem menos saída do que a entrada de leitura ou, mais precisamente, comandos que não lêem dados que eles escreveram anteriormente.

E zsh tem a forma =(...) de substituição de processo que você pode usar como:

 mv =(tr 01 10 < file) file

(com efeito e advertências similares a ksh93 's >; ). Ou:

 cp =(tr 01 10 < file) file

que preservaria os atributos de file , mas significa uma cópia extra.

Agora, se você precisar ler e escrever no mesmo deslocamento usando o mesmo descritor de arquivo e nem o zsh nem o ksh93 estiverem disponíveis, você sempre poderá reverter para perl / python / ruby ...

perl -e '
  open F, "<>", "file" or die "open: $!";
  read F, $var, 1;
  seek F, 0, 0;
  print F "something-else"'

Agora, depois de reler a versão atualizada da sua pergunta, parece que seu arquivo está se comportando mais como um soquete ou canal bidirecional e não como um arquivo normal e pesquisável.

Nesse caso, pode ser apenas uma questão de:

socat - file:your-file

ou:

(cat >&3 3>&- & cat <&3 3<&-) 3<> your-file

para alimentar dados de e para esse arquivo como lidos de / para stdin / stdout.

Note que cada cat lê / escreve em sua própria cópia do descritor de arquivo 3 aberto pelo shell, mas eles compartilham a mesma descrição do arquivo aberto , portanto deve ser equivalente.

    
por 21.03.2016 / 10:40
0

A menos que você esteja traduzindo somente , você não pode ler e escrever ao mesmo tempo com segurança (e mesmo com a tradução, é possível que o arquivo possa ser traduzido pela metade se o programa sai com um erro).

Parece que você realmente deseja alguma combinação de tee e sponge . Você pode usar sponge de moreutils para absorver a entrada padrão e depois escrevê-la em um arquivo. Você pode usar tee para duplicar a entrada para várias saídas.

O resultado final seria algo como isto:

command < filename | tee >(sponge filename)

>() é um exemplo de substituição de processos . Nesse caso, >(sponge filename) é substituído pelo caminho para um FIFO que vai para o stdin de sponge , ao qual tee grava. tee também grava sua entrada para stdout independentemente.

Aqui está um exemplo. Você pode ver que a saída é gravada no stdout e gravada no arquivo.

$ echo 'foo bar' > file
$ sed 's/foo/qux/' < file | tee >(sponge file)
qux bar
$ cat file
qux bar
    
por 20.03.2016 / 21:40