Esto es muy posible, solo tiene que asegurarse de que para cuando escriba la salida, la esté escribiendo en un archivo diferente. Esto se puede hacer eliminando el archivo después de abrirle un descriptor de archivo, pero antes de escribir en él:
exec 3<file ; rm file; COMMAND <&3 >file ; exec 3>&-
O línea por línea, para entenderlo mejor:
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
Todavía es algo arriesgado, porque si COMMAND no se ejecuta correctamente, perderá el contenido del archivo. Eso se puede mitigar restaurando el archivo si COMMAND devuelve un código de salida distinto de cero:
exec 3<file ; rm file; COMMAND <&3 >file || cat <&3 >file ; exec 3>&-
También podemos definir una función de shell para que sea más fácil de usar:
# Usage: replace FILE COMMAND
replace() { exec 3<$1 ; rm $1; ${@:2} <&3 >$1 || cat <&3 >$1 ; exec 3>&- }
Ejemplo:
$ echo aaa > test
$ replace test tr a b
$ cat test
bbb
Además, tenga en cuenta que esto mantendrá una copia completa del archivo original (hasta que se cierre el tercer descriptor de archivo). Si está utilizando Linux y el archivo en el que está procesando es demasiado grande para caber dos veces en el disco, puede consultar este script que canalizará el archivo al comando especificado bloque por bloque mientras desasigna el ya procesado bloques. Como siempre, lea las advertencias en la página de uso.