Poder-se-ia 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
não truncará file
.
Tudo depende de qual comando é processado primeiro, pois, ao contrário do que se espera, os comandos em um pipe não são processado da esquerda para a direita . Não há nenhuma garantia sobre qual comando será escolhido primeiro, então pode-se apenas pensar nisso como escolhido aleatoriamente e nunca confiar no shell não escolhendo o ofensivo.
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
Então nunca usa algo como cat file | some_sed_command | tee file >/dev/null
. Use sponge
como sugerido por Oli.
Como alternativa, para ambientes mais rigorosos 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