¿Cómo puedo usar un archivo en un comando y redireccionar el resultado al mismo archivo sin truncarlo?

Básicamente, quiero tomar como entrada el texto de un archivo, eliminar una línea de ese archivo y enviar la salida al mismo archivo. Algo en esta línea si eso lo aclara.

grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name > file_name 

sin embargo, cuando hago esto termino con un archivo en blanco. ¿Alguna idea?

No puede hacer eso porque bash procesa primero las redirecciones y luego ejecuta el comando. Entonces, cuando grep mira file_name, ya está vacío. Sin embargo, puedes usar un archivo temporal.

 grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name > tmpfile cat tmpfile > file_name 

así, considere usar mktemp para crear el tmpfile pero tenga en cuenta que no es POSIX.

Use una esponja para este tipo de tareas. Es parte de moreutils.

Pruebe este comando:

  grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name | sponge file_name 

Use sed en su lugar:

 sed -i '/seg[0-9]\{1,\}\.[0-9]\{1\}/d' file_name 

intenta esto simple

 grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name | tee file_name 

Su archivo no estará en blanco esta vez 🙂 y su resultado también se imprimirá en su terminal.

No puede usar el operador de redirección ( > o >> ) en el mismo archivo, porque tiene una precedencia más alta y creará / truncará el archivo incluso antes de invocar el comando. Para evitar eso, debe usar las herramientas apropiadas, como tee , sponge , sed -i o cualquier otra herramienta que pueda escribir resultados en el archivo (por ejemplo, sort file -o file ).

Básicamente, redirigir la entrada al mismo archivo original no tiene sentido y debe usar editores in situ apropiados para eso, por ejemplo, el editor Ex (parte de Vim):

 ex '+g/seg[0-9]\{1,\}\.[0-9]\{1\}/d' -scwq file_name 

dónde:

  • '+cmd' / -c – ejecuta cualquier comando Ex / Vim
  • g/pattern/d – eliminar líneas que coincidan con un patrón usando global ( help :g )
  • -s – modo silencioso ( man ex )
  • -c wq – execute :write y :quit comandos

Puede usar sed para lograr lo mismo (como ya se ha mostrado en otras respuestas), sin embargo in-situ ( -i ) es una extensión de FreeBSD no estándar (puede funcionar de forma diferente entre Unix / Linux) y básicamente es un editor sísmico, no un editor de archivos. Ver: ¿El modo Ex tiene algún uso práctico?

Una alternativa de línea alternativa: configure el contenido del archivo como variable:

 VAR=`cat file_name`; echo "$VAR"|grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' > file_name 

También hay ed (como una alternativa a sed -i ):

 # cf. http://wiki.bash-hackers.org/howto/edit-ed printf '%s\n' H 'g/seg[0-9]\{1,\}\.[0-9]\{1\}/d' wq | ed -s file_name 

Puedes hacer eso usando la sustitución del proceso .

Sin embargo, es un poco complicado, ya que bash abre todas las canalizaciones de forma asíncrona y tenemos que trabajar en eso utilizando sleep modo YMMV.

En tu ejemplo:

 grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name > >(sleep 1 && cat > file_name) 
  • >(sleep 1 && cat > file_name) crea un archivo temporal que recibe la salida de grep
  • sleep 1 demora por un segundo para dar tiempo grep para analizar el archivo de entrada
  • finalmente cat > file_name escribe la salida

Puedes usar slurp con POSIX Awk:

 !/seg[0-9]\{1,\}\.[0-9]\{1\}/ { q = q ? q RS $0 : $0 } END { print q > ARGV[1] } 

Ejemplo

Usualmente uso el progtwig de tee para hacer esto:

 grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name | tee file_name 

Crea y elimina un archivo temporal por sí mismo.

Prueba esto

 echo -e "AAA\nBBB\nCCC" > testfile cat testfile AAA BBB CCC echo "$(grep -v 'AAA' testfile)" > testfile cat testfile BBB CCC 

tal vez puedas hacerlo así:

 grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name | cat > file_name