¿Puedo hacer que 'cortar' cambie un archivo en su lugar?

Respuestas:

14

No puedes Utilice ed o GNU sed o perl, o haga lo que hacen detrás de escena, que es crear un nuevo archivo para el contenido.

ed, portátil:

ed foo <<EOF
1,$s/^\([^,]*\),\([^,]*\),\([^,]*\).*/\1,\3/
w
q
EOF

GNU sed:

sed -i -e 's/^\([^,]*\),\([^,]*\),\([^,]*\).*/\1,\3/' foo

Perl:

perl -i -l -F, -pae 'print @F[1,3]' foo

cut, creando un nuevo archivo (recomendado, porque si su secuencia de comandos se interrumpe, puede ejecutarla nuevamente):

cut -d , -f 1,3 <foo >foo.new &&
mv -f foo.new foo

cut, reemplazando el archivo en su lugar (conserva la propiedad y los permisos de foo, pero necesita protección contra interrupciones):

cp -f foo foo.old &&
cut -d , -f 1,3 <foo.old >foo &&
rm foo.old

Recomiendo usar uno de los cutmétodos basados. De esa manera, no depende de ninguna herramienta no estándar, puede usar la mejor herramienta para el trabajo y controlar el comportamiento en la interrupción.

Gilles 'SO- deja de ser malvado'
fuente
Mejor que el .oldmétodo para los cambios en el lugar,echo "$(cut -d , -f 1,3 <foo)" > foo
GypsyCosmonaut
1
@GypsyCosmonaut No, esto no es "mejor". Es mas frágil. Su único beneficio es que es más corto de escribir. El principal problema con su método es que si ocurre un error al procesar el archivo de entrada o al escribir la salida, los datos se pierden. Tampoco funciona con datos binarios: la salida se truncará en el primer byte nulo. Incluso con archivos de texto, elimina las líneas vacías del final del archivo. Con archivos grandes, puede fallar porque los datos deben almacenarse como una cadena en la memoria del shell (y recuerde, si esto sucede, los datos se pierden).
Gilles 'SO- deja de ser malvado'
oO Gracias, no sabía que podría haber problemas con el byte nulo
GypsyCosmonaut
Creo que esto es más simple:cut -d , -f 1,3 foo > foo.new rm foo mv foo.new foo
LoMaPh
@LoMaPh De hecho, no sé por qué cambié el nombre del archivo antiguo: no tiene ninguna ventaja sobre el cambio de nombre del nuevo archivo. También es más simple porque no necesitas el paso rm foo. Y no debe llamar rm foo, porque mv foo.new fooes atómico: elimina la versión anterior y coloca la nueva versión al mismo tiempo.
Gilles 'SO- deja de ser malvado'
23

El paquete moreutils de ubuntu (y también debian ) tiene un programa llamado sponge, que también resuelve su problema.

De hombre esponja:

la esponja lee la entrada estándar y la escribe en el archivo especificado. A diferencia de una redirección de shell, la esponja absorbe toda su entrada antes de abrir el archivo de salida. Esto permite restringir las tuberías que leen y escriben en el mismo archivo.

Lo que te permitiría hacer algo como:

cut -d <delim> -f <fields> somefile | sponge somefile
Kjetil Jorgensen
fuente
9

No creo que sea posible usar cutsolo. No pude encontrarlo en el hombre o en la página de información. Puedes hacer algo como

mytemp=$(mktemp) && cut -d" " -f1 file > $mytemp && mv $mytemp file

mktemplo convierte en un archivo temporal relativamente seguro en el que puede canalizar la cutsalida.

Steven D
fuente
1

Prueba vim-way:

$ ex -s +'%!cut -c 1-10' -cxa file.txt

Esto editará el archivo en el lugar (así que primero haga la copia de seguridad).

Alternativamente grep, use , sedo gawk.

kenorb
fuente
0

Puede usar slurp con POSIX Awk:

cut -b1 file | awk 'BEGIN{RS="";getline<"-";print>ARGV[1]}' file

Ejemplo

Steven Penny
fuente
0

Bueno, dado que cutproduce menos salida de lo que lee, puede hacer:

cut -c1 < file 1<> file

Es decir, haga que su entrada estándar sea fileabierta en modo de solo lectura y que sea fileabierta en modo lectura + escritura sin truncamiento ( <>).

De esa manera, cutsimplemente sobrescribirá el archivo sobre sí mismo. Sin embargo, dejará el resto del archivo intacto. Por ejemplo, si filecontiene:

foo
bar

La salida se convertirá en:

f
b
bar

La f\nb\nhan reemplazado foo\n, pero bartodavía está allí. Debería truncar el archivo una vez que cuthaya finalizado.

Con ksh93, puede hacerlo con su <>;operador, que actúa como, <>excepto que si el comando tiene éxito, ftruncate()se llama en el descriptor de archivo. Entonces:

cut -c1 < file 1<>; file

Con otros proyectiles, deberías hacerlo a ftruncate()través de otros medios como:

{ cut -c1 < file; perl -e 'truncate STDOUT, tell STDOUT';} 1<> file

aunque invocar perlsolo por eso es un poco exagerado, especialmente teniendo en cuenta que perlpuede hacer fácilmente ese cuttrabajo como:

perl -pi -e '$_ = substr($_, 0, 1)' file

Tenga en cuenta que con todos los métodos que implican una reescritura real en el lugar, si la operación se interrumpe a mitad de camino, terminará con un archivo dañado. El uso de un segundo archivo temporal evita este problema.

Stéphane Chazelas
fuente