Alguém poderia pensar que
echo foo >a
cat a | rev >a
deixaria a
contendo oof
; mas em vez disso é deixado vazio.
- Por quê?
- Como se aplicaria
rev
de outra formaa
?
Alguém poderia pensar que
echo foo >a
cat a | rev >a
deixaria a
contendo oof
; mas em vez disso é deixado vazio.
rev
de outra forma a
?
Existe um aplicativo para isso! O
sponge
comando frommoreutils
é projetado exatamente para isso. Se você estiver executando o Linux, provavelmente já está instalado, caso contrário, procure nos repositórios do seu sistema operacionalsponge
oumoreutils
. Então, você pode fazer:Ou, evitando o UUoC :
A razão para esse comportamento está na ordem em que seus comandos são executados. O
> a
é realmente a primeira coisa executada e> file
esvazia o arquivo. Por exemplo:Então, quando você executa
cat a | rev >a
o que realmente acontece é que o> a
é executado primeiro, esvaziando o arquivo, então quando ocat a
é executado o arquivo já está vazio. Justamente por issosponge
foi escrito (deman sponge
, grifo meu):outra maneira de corrigir isso é usar um método de escrita que não trunque
isso só funciona porque:
rev lê o conteúdo antes de produzir a saída e a saída nunca é maior do que a quantidade já lida
o novo conteúdo do arquivo é do mesmo tamanho ou maior que o original (neste caso, mesmo tamanho)
Essa abordagem pode ser útil para modificação no local de arquivos muito grandes para manter cópias temporárias.
No pipeline acima, o shell bifurca dois subprocessos, um para cada uma das duas partes do pipeline. Esses subprocessos executam os comandos em questão, primeiro processando qualquer redirecionamento e, em seguida, chamando uma das
exec*()
funções para iniciar o utilitário externo. Os subprocessos são executados em paralelo e não há garantias de tempo entre eles .A execução de um processo não é muito rápida, então geralmente o que acontece é que o shell do lado direito consegue configurar o redirecionamento antes
cat
de ter a chance de ler o arquivo. O redirecionamento de saída> a
trunca o arquivo, portantocat
, não tem nada para ler,rev
não recebe dados e não produz dados. Mesmo se você usar um redirecionamento do lado esquerdo também (cat < a | rev > a
),a
pode ser aberto para leitura antes de ser truncado, mascat
ainda assim provavelmente não teria tempo para lê-lo antes disso.Por outro lado, isso é impresso de forma bastante consistente
a contains: foo
no meu sistema:Aqui é
tee
que trunca o arquivo, então isso acontece depois doexec()
ecat
tem mais chance de ter tempo de ler o arquivo. Mas se o arquivo for grande o suficiente, ele pode ficar truncado no meio da leitura.Eu disse pode e provavelmente existe, porque de fato o oposto pode acontecer, se o SO decidir agendar os processos de outra forma.
A solução usual é usar um arquivo temporário:
Embora haja o problema usual de possivelmente substituir um arquivo existente, a menos que você possa ter certeza de que o nome do arquivo temporário está disponível. Você provavelmente deve usar
mktemp
:Como alternativa, você pode usar a
sponge
ferramenta , que garante a leitura de todas as entradas recebidas antes de abrir o arquivo de saída (caso contrário, é comocat
):ou apenas
sponge > a
seria um erro pela mesma razão que o comando original não funciona.A esponja é de moreutils e não uma ferramenta padrão. Algumas alternativas para ele estão listadas em Completamente buffer de saída do comando antes de canalizar para outro comando?
Alguns utilitários podem implementar um recurso semelhante, por exemplo,
sort -o outputfile
só abre o arquivo de saída após terminar, veja O sort suporta a ordenação de um arquivo no local, como `sed --in-place`?>file
cria um novo arquivo vazio ou trunca um arquivo existente. Como tal, não há mais nada no arquivo pararev
ler.Como outras respostas mencionaram, você pode usar
sponge
para isso. Massponge
não está disponível para todos.O seguinte é uma solução genérica que envolve apenas o shell:
Isso abre o arquivo (como fd 3) e o exclui. Mais precisamente, isso exclui apenas a entrada de diretório do arquivo, não o arquivo em si. O arquivo não será excluído até que todos os links físicos sejam excluídos e todos os identificadores sejam fechados.
Em seguida,
rev
é executada a leitura do "arquivo excluído". Sua saída é enviada para um novo arquivo. Embora esse novo arquivo tenha o mesmo nome do arquivo original, é um arquivo diferente. Como tal, não há conflito.Por fim, fechamos o descritor para o arquivo original, permitindo que ele seja liberado.
O problema com a abordagem acima é que os dados são perdidos se ocorrer um problema. É por isso que se pode preferir o seguinte (que não usa mais espaço em disco do que o acima):
Você pode usar o Vim no modo Ex:
%
selecione todas as linhas!
comando de execuçãox
salvar e fecharAs razões foram explicadas por outros, mas basicamente a mesma razão pela qual você não pode fazer:
Mas como alguém pode conseguir isso: