¿Cómo ejecuto cualquier comando que edite su archivo (argumento) & # 8220; in place & # 8221; usando bash?
-
02-07-2019 - |
Pregunta
Tengo un archivo temp.txt, que quiero ordenar con el comando sort
en bash.
Quiero que los resultados ordenados reemplacen el archivo original.
Esto no funciona, por ejemplo (obtengo un archivo vacío):
sortx temp.txt > temp.txt
¿Se puede hacer esto en una línea sin tener que copiar a archivos temporales?
EDITAR: La opción -o
es muy buena para sort
. Utilicé sort
en mi pregunta como ejemplo. Me encuentro con el mismo problema con otros comandos:
uniq temp.txt > temp.txt.
¿Hay una mejor solución general?
Solución
sort temp.txt -o temp.txt
Otros consejos
Un sort
necesita ver todas las entradas antes de que pueda comenzar a generar. Por este motivo, el programa sort
puede ofrecer fácilmente una opción para modificar un archivo in situ:
sort temp.txt -o temp.txt
Específicamente, la documentación de GNU sort
dice:
Normalmente, la ordenación lee todas las entradas antes de abrir el archivo de salida, por lo que puede ordenar un archivo en forma segura usando comandos como
sort -o F F
ycat F | ordenar -o F
. Sin embargo,sort
con--merge
(-m
) puede abrir el archivo de salida antes de leer todas las entradas, por lo que un comando comocat F | sort -m -o F - G
no es seguro ya que sort podría comenzar a escribirF
antes de quecat
termine de leerlo.
Mientras que la documentación de BSD sort
dice:
Los comandosSi [el] archivo de salida es uno de los archivos de entrada, la clasificación lo copia en un archivo temporal antes de ordenar y escribir la salida en [el] archivo de salida.
como uniq
pueden comenzar a escribir la salida antes de que terminen de leer la entrada. Estos comandos normalmente no admiten la edición en contexto (y sería más difícil para ellos admitir esta función).
Normalmente, solucionas esto con un archivo temporal, o si quieres evitar tener un archivo intermedio, puedes usar un búfer para almacenar el resultado completo antes de escribirlo. Por ejemplo, con perl
:
uniq temp.txt | perl -e 'undef $/; Un sort
necesita ver todas las entradas antes de que pueda comenzar a generar. Por este motivo, el programa sort
puede ofrecer fácilmente una opción para modificar un archivo in situ:
sort temp.txt -o temp.txt
Específicamente, la documentación de GNU sort
dice:
Normalmente, la ordenación lee todas las entradas antes de abrir el archivo de salida, por lo que puede ordenar un archivo en forma segura usando comandos como sort -o F F
y cat F | ordenar -o F
. Sin embargo, sort
con --merge
( -m
) puede abrir el archivo de salida antes de leer todas las entradas, por lo que un comando como cat F | sort -m -o F - G
no es seguro ya que sort podría comenzar a escribir F
antes de que cat
termine de leerlo.
Mientras que la documentación de BSD sort
dice:
Si [el] archivo de salida es uno de los archivos de entrada, la clasificación lo copia en un archivo temporal antes de ordenar y escribir la salida en [el] archivo de salida.
Los comandos como uniq
pueden comenzar a escribir la salida antes de que terminen de leer la entrada. Estos comandos normalmente no admiten la edición en contexto (y sería más difícil para ellos admitir esta función).
Normalmente, solucionas esto con un archivo temporal, o si quieres evitar tener un archivo intermedio, puedes usar un búfer para almacenar el resultado completo antes de escribirlo. Por ejemplo, con perl
:
<*>
Aquí, la parte de perl lee la salida completa de uniq
en la variable $ _
y luego sobrescribe el archivo original con estos datos. Podrías hacer lo mismo en el lenguaje de script de tu elección, tal vez incluso en Bash. Pero tenga en cuenta que necesitará suficiente memoria para almacenar el archivo completo, esto no es recomendable cuando se trabaja con archivos grandes.
= <>; open(OUT,">temp.txt"); print OUT;'
Aquí, la parte de perl lee la salida completa de uniq
en la variable $ _
y luego sobrescribe el archivo original con estos datos. Podrías hacer lo mismo en el lenguaje de script de tu elección, tal vez incluso en Bash. Pero tenga en cuenta que necesitará suficiente memoria para almacenar el archivo completo, esto no es recomendable cuando se trabaja con archivos grandes.
Este es un enfoque más general, funciona con uniq, sort and whatnot.
{ rm file && uniq > file; } < file
El comentario de Tobu sobre la esponja garantiza ser una respuesta por derecho propio.
Para citar de la másutils página de inicio:
Probablemente, la herramienta de propósito más general en moreutils hasta ahora es sponge (1), que te permite hacer cosas como estas:
% sed "s/root/toor/" /etc/passwd | grep -v joey | sponge /etc/passwd
Sin embargo, sponge
tiene el mismo problema Steve Jessop comenta aquí. Si hay alguno. de los comandos en la tubería antes de que sponge
falle, el archivo original se sobrescribirá.
$ mistyped_command my-important-file | sponge my-important-file
mistyped-command: command not found
Uh-oh, my-important-file
se ha ido.
Aquí tienes, una línea:
sort temp.txt > temp.txt.sort && mv temp.txt.sort temp.txt
Técnicamente no hay copia en un archivo temporal, y el comando 'mv' debería ser instantáneo.
Me gusta la respuesta de sort file -o file
pero no quiero escribir el mismo nombre de archivo dos veces.
Uso de BASH expansión del historial :
$ sort file -o !#^
toma el primer argumento de la línea actual cuando presionas enter .
Una clasificación única in situ:
$ sort -u -o file !#$
toma el último argumento de la línea actual.
Muchos han mencionado la opción -o . Aquí está la parte de la página de manual.
Desde la página del manual:
-o output-file
Write output to output-file instead of to the standard output.
If output-file is one of the input files, sort copies it to a
temporary file before sorting and writing the output to output-
file.
Esto tendría una gran capacidad de memoria, pero podría usar awk para almacenar los datos intermedios en la memoria y luego volver a escribirlos.
uniq temp.txt | awk '{line[i++] = <*>}END{for(j=0;j<i;j++){print line[j]}}' > temp.txt
Una alternativa a sponge
con el sed
más común:
sed -ni r<(command file) file
Funciona para cualquier comando ( sort
, uniq
, tac
, ...) y utiliza el muy conocido sed La opción
(editar archivos en el lugar). -i
de
Advertencia: Intente comando file
primero porque la edición de archivos en el lugar no es segura por naturaleza.
Explicación
En primer lugar, le está diciendo a sed
que no imprima las líneas (originales) ( -n
opción ), y con la ayuda de sed
's r
comando y bash
's Process Sustitution , el contenido generado por < (archivo de comandos)
será la salida guardada en su lugar .
Haciendo las cosas aún más fáciles
Puedes incluir esta solución en una función:
ip_cmd() { # in place command
CMD=${1:?You must specify a command}
FILE=${2:?You must specify a file}
sed -ni r<("$CMD" "$FILE") "$FILE"
}
Ejemplo
$ cat file
d
b
c
b
a
$ ip_cmd sort file
$ cat file
a
b
b
c
d
$ ip_cmd uniq file
$ cat file
a
b
c
d
$ ip_cmd tac file
$ cat file
d
c
b
a
$ ip_cmd
bash: 1: You must specify a command
$ ip_cmd uniq
bash: 2: You must specify a file
Utilice el argumento --output =
o -o
Sólo probé en FreeBSD:
sort temp.txt -otemp.txt
Para agregar la capacidad uniq
, ¿cuáles son los inconvenientes de:
sort inputfile | uniq | sort -o inputfile
Lea sobre el editor no interactivo, ex
.
Si insiste en usar el programa sort
, tiene que usar un archivo intermedio. No creo que sort
tenga una opción para clasificar en la memoria. Cualquier otro truco con stdin / stdout fallará a menos que pueda garantizar que el tamaño del búfer para el stdin de clasificación sea lo suficientemente grande como para que quepa todo el archivo.
Edit: la vergüenza de mí. sort temp.txt -o temp.txt
funciona excelente.
Otra solución:
uniq file 1<> file