¿Cómo ejecuto cualquier comando que edite su archivo (argumento) & # 8220; in place & # 8221; usando bash?

StackOverflow https://stackoverflow.com/questions/146435

  •  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?

¿Fue útil?

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 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 :

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 -i de (editar archivos en el lugar).

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
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top