Isso é muito possível, você só precisa ter certeza de que, no momento de escrever a saída, está gravando em um arquivo diferente. Isso pode ser feito removendo o arquivo depois de abrir um descritor de arquivo nele, mas antes de gravá-lo:
exec 3<file ; rm file; COMMAND <&3 >file ; exec 3>&-
Ou linha por linha, para entender melhor:
exec 3<file # open a file descriptor reading 'file'
rm file # remove file (but fd3 will still point to the removed file)
COMMAND <&3 >file # run command, with the removed file as input
exec 3>&- # close the file descriptor
Ainda é uma coisa arriscada de se fazer, porque se COMMAND não funcionar corretamente, você perderá o conteúdo do arquivo. Isso pode ser atenuado restaurando o arquivo se COMMAND retornar um código de saída diferente de zero:
exec 3<file ; rm file; COMMAND <&3 >file || cat <&3 >file ; exec 3>&-
Também podemos definir uma função shell para torná-la mais fácil de usar:
# Usage: replace FILE COMMAND
replace() { exec 3<$1 ; rm $1; ${@:2} <&3 >$1 || cat <&3 >$1 ; exec 3>&- }
Exemplo:
$ echo aaa > test
$ replace test tr a b
$ cat test
bbb
Além disso, observe que isso manterá uma cópia completa do arquivo original (até que o terceiro descritor de arquivo seja fechado). Se você estiver usando Linux e o arquivo no qual está processando for muito grande para caber duas vezes no disco, você pode verificar este script que canalizará o arquivo para o comando especificado bloco a bloco enquanto desaloca o já processado blocos. Como sempre, leia os avisos na página de uso.