bash - salida - tuberias linux
¿Cómo puedo usar un archivo en un comando y redireccionar el resultado al mismo archivo sin truncarlo? (11)
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.
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?
Puedes hacer eso usando process-substitution .
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]
}
Example
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
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
Use sed en su lugar:
sed -i ''/seg[0-9]/{1,/}/.[0-9]/{1/}/d'' file_name
Use una sponge 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
Usualmente uso el programa 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.
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.
tal vez puedas hacerlo así:
grep -v ''seg[0-9]/{1,/}/.[0-9]/{1/}'' file_name | cat > file_name