Pode-se resolver o problema da seguinte maneira:
cat file | some_sed_command | tee file >/dev/null
Não .
As chances file
serão truncadas, mas não há garantia cat file | some_sed_command | tee file >/dev/null
que não será truncada file
.
Tudo depende de qual comando é processado primeiro, ao contrário do que se pode esperar, os comandos em um canal não são processados da esquerda para a direita . Não há garantia sobre qual comando será escolhido primeiro; portanto, pode-se pensar nele como escolhido aleatoriamente e nunca confiar que o shell não escolha o ofensor.
Como as chances de o comando incorreto ser escolhido primeiro entre três comandos são menores do que as chances de o comando incorreto ser escolhido primeiro entre dois comandos, é menos provável que file
seja truncado, mas ainda vai acontecer .
script.sh
:
#!/bin/bash
for ((i=0; i<100; i++)); do
cat >file <<-EOF
foo
bar
EOF
cat file |
sed 's/bar/baz/' |
tee file >/dev/null
[ -s file ] &&
echo 'Not truncated' ||
echo 'Truncated'
done |
sort |
uniq -c
rm file
% bash script.sh
93 Not truncated
7 Truncated
% bash script.sh
98 Not truncated
2 Truncated
% bash script.sh
100 Not truncated
Portanto, nunca use algo como cat file | some_sed_command | tee file >/dev/null
. Use sponge
como Oli sugeriu.
Como alternativa, para ambientes mais sofisticados e / ou arquivos relativamente pequenos, pode-se usar uma string here e uma substituição de comando para ler o arquivo antes que qualquer comando seja executado:
$ cat file
foo
bar
$ for ((i=0; i<100; i++)); do <<<"$(<file)" sed 's/bar/baz/' >file; done
$ cat file
foo
baz