Shell un liner para anteponer a un archivo


Esto es probablemente una solución compleja.

Estoy buscando un operador simple como ">>", pero para anteponiendo.

Me temo que no existe. Voy a tener que hacer algo como

 mv myfile tmp
 cat myheader tmp > myfile

¿Algo más inteligente?

 122
Author: codeforester, 2008-09-10

30 answers

El hack a continuación fue una rápida respuesta improvisada que funcionó y recibió muchos votos positivos. Luego, a medida que la pregunta se hizo más popular y pasó más tiempo, la gente indignada comenzó a informar que funcionaba, pero que podían pasar cosas raras, o simplemente no funcionaba en absoluto, por lo que fue rechazada furiosamente durante un tiempo. Qué divertido.

La solución explota la implementación exacta de descriptores de archivos en su sistema y, debido a que la implementación varía significativamente entre nixes, su éxito es totalmente dependiente del sistema, definitivamente no portátil, y no debe confiarse en nada incluso vagamente importante.

Ahora, con todo eso fuera del camino, la respuesta fue:


Crear otro descriptor de fichero para el fichero (exec 3<> yourfile) y luego escribir a ese (>&3) parece superar el dilema de lectura/escritura en el mismo fichero. Funciona para mí en 600K archivos con awk. Sin embargo, intentar el mismo truco usando ' cat ' falla.

Pasando el antepasado como una variable a awk (-v TEXT="$text") supera el problema de las citas literales que impide hacer este truco con 'sed'.

#!/bin/bash
text="Hello world
What's up?"

exec 3<> yourfile && awk -v TEXT="$text" 'BEGIN {print TEXT}{print}' yourfile >&3
 25
Author: John Mee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-29 09:18:25

Esto todavía utiliza un archivo temporal, pero al menos está en una línea:

echo "text" | cat - yourfile > /tmp/out && mv /tmp/out yourfile

Crédito: BASH: Anteponer Un Texto / Líneas A un Archivo

 91
Author: Jason Navarrete,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-02-02 06:44:32
echo '0a
your text here
.
w' | ed some_file

Ed es el Editor Estándar! http://www.gnu.org/fun/jokes/ed.msg.html

 28
Author: fluffle,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-07-17 16:24:17

John Mee: su método no está garantizado para funcionar, y probablemente fallará si antepone más de 4096 bytes de cosas (al menos eso es lo que sucede con gnu awk, pero supongo que otras implementaciones tendrán restricciones similares). No solo fallará en ese caso, sino que entrará en un bucle sin fin donde leerá su propia salida, haciendo que el archivo crezca hasta que se llene todo el espacio disponible.

Pruébalo por ti mismo:

exec 3<>myfile && awk 'BEGIN{for(i=1;i<=1100;i++)print i}{print}' myfile >&3

(advertencia: mátalo después de un tiempo o llenará el sistema de archivos)

Por otra parte, es muy peligroso editar archivos de esa manera, y es muy mal consejo, como si algo sucede mientras el archivo se está editando (bloqueo, disco lleno) que está casi garantizado para ser dejado con el archivo en un estado inconsistente.

 20
Author: anonymous,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-12-18 06:17:05

Vale la pena señalar que a menudo es una buena idea generar de forma segura el archivo temporal utilizando una utilidad como mktemp, al menos si el script se ejecutará alguna vez con privilegios de root. Por ejemplo, podría hacer lo siguiente (de nuevo en bash):

(tmpfile=`mktemp` && { echo "prepended text" | cat - yourfile > $tmpfile && mv $tmpfile yourfile; } )
 15
Author: ehdr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-12 16:51:54

No es posible sin un archivo temporal, pero aquí va un oneliner

{ echo foo; cat oldfile; } > newfile && mv newfile oldfile

Puede usar otras herramientas como ed o perl para hacerlo sin archivos temporales.

 14
Author: Vinko Vrsalovic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-10 15:30:04

Si necesita esto en los equipos que controla, instale el paquete "moreutils" y use "sponge". Entonces puedes hacer:

cat header myfile | sponge myfile
 12
Author: user2227573,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-03-30 17:10:17

Usando un bash heredoc puede evitar la necesidad de un archivo tmp:

cat <<-EOF > myfile
  $(echo this is prepended)
  $(cat myfile)
EOF

Esto funciona porque $(cat myfile) se evalúa cuando se evalúa el script bash, antes de que se ejecute el cat con redireccionamiento.

 10
Author: Eric Woodruff,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-02-13 16:41:08

Suponiendo que el archivo que desea editar mi.txt

$cat my.txt    
this is the regular file

Y el archivo que desea anteponer es header

$ cat header
this is the header

Asegúrese de tener una última línea en blanco en el archivo de encabezado.
Ahora puedes anteponerlo con

$cat header <(cat my.txt) > my.txt

Terminas con

$ cat my.txt
this is the header
this is the regular file

Hasta donde yo sé esto solo funciona en 'bash'.

 9
Author: cb0,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-16 14:09:13

Cuando comienzas a intentar hacer cosas que se vuelven difíciles en shell-script, te sugiero encarecidamente que busques reescribir el script en un lenguaje de scripting "adecuado" (Python/Perl/Ruby/etc)

En cuanto a anteponer una línea a un archivo, no es posible hacer esto a través de tuberías, ya que cuando haces algo como cat blah.txt | grep something > blah.txt, inadvertidamente se vacía el archivo. Hay un pequeño comando de utilidad llamado sponge que puede instalar (lo hace cat blah.txt | grep something | sponge blah.txt y almacena en búfer el contenido del archivo, luego lo escribe en el file). Es similar a un archivo temporal pero no tiene que hacerlo explícitamente. pero yo diría que es un requisito" peor " que, por ejemplo, Perl.

Puede haber una manera de hacerlo a través de awk, o similar, pero si tiene que usar shell-script, creo que un archivo temporal es, con mucho, el más fácil(/only?) manera..

 9
Author: dbr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-12-18 06:19:43

EDITAR: Esto está roto. Ver Comportamiento extraño al anteponer un archivo con cat y tee

La solución para el problema de sobrescritura es usar tee:

cat header main | tee main > /dev/null
 9
Author: Daniel Velkov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:47:18

Como Daniel Velkov sugiere, usa tee.
Para mí, esa es una solución inteligente simple:

{ echo foo; cat bar; } | tee bar > /dev/null
 7
Author: Max Tsepkov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-02-28 16:06:42

El que yo uso. Este le permite especificar orden, caracteres adicionales, etc. de la manera que le guste:

echo -e "TEXTFIRSt\n$(< header)\n$(< my.txt)" > my.txt

P.d.: solo que no funciona si los archivos contienen texto con barra invertida, porque se interpreta como caracteres de escape

 3
Author: nemisj,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-24 09:35:49

Principalmente por diversión/shell golf, pero

ex -c '0r myheader|x' myfile

Hará el truco, y no hay canalizaciones o redirecciones. Por supuesto, vi / ex no es realmente para uso no interactivo, por lo que vi parpadeará brevemente.

 3
Author: benjwadams,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-07-08 17:21:32

¿Por qué no simplemente usar el comando ed (como ya sugirió fluffle aquí)?

Ed lee todo el archivo en la memoria y realiza automáticamente una edición del archivo en el lugar!

Entonces, si su archivo no es tan grande ...

# cf. "Editing files with the ed text editor from scripts.",
# http://wiki.bash-hackers.org/doku.php?id=howto:edit-ed

prepend() {
   printf '%s\n' H 1i "${1}" . wq | ed -s "${2}"
}

echo 'Hello, world!' > myfile
prepend 'line to prepend' myfile

Otra solución sería usar manejadores de archivos abiertos como sugirió Jürgen Hötzel en Redirigir la salida de sed 's/c/ d /' mi archivo a mi archivo

echo cat > manipulate.txt
exec 3<manipulate.txt
# Prevent open file from being truncated:
rm manipulate.txt
sed 's/cat/dog/' <&3 > manipulate.txt

Todo esto podría ser puesto en una sola línea, por supuesto.

 2
Author: torf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:03:02

Una variante de la solución de cb0 para" sin archivo temporal " para anteponer texto fijo:

echo "text to prepend" | cat - file_to_be_modified | ( cat > file_to_be_modified ) 

De nuevo esto se basa en la ejecución de sub-shell - el (..)- para evitar que el gato se niegue a tener el mismo archivo de entrada y salida.

Nota: Me gustó esta solución. Sin embargo, en mi Mac el archivo original se pierde (pensé que no debería, pero lo hace). Eso podría arreglarse escribiendo su solución como: echo " text to prepend | | cat - file_to_be_modified / cat > tmp_file; mv tmp_file file_to_be_modified

 2
Author: Alberto A. Medina,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-06-10 19:41:36

Esto es lo que descubrí:

echo -e "header \n$(cat file)" >file
 2
Author: Hammad Akhwand,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-11-21 07:34:47
sed -i -e '1rmyheader' -e '1{h;d}' -e '2{x;G}' myfile
 2
Author: weakish,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-05-06 15:30:20

ADVERTENCIA: esto necesita un poco más de trabajo para satisfacer las necesidades de la OP.

Debería haber una manera de hacer que el enfoque sed de @shixilun funcione a pesar de sus dudas. Debe haber una orden bash para escapar de los espacios en blanco al leer un archivo en una cadena de sustitución de sed (por ejemplo, reemplazar caracteres de nueva línea con '\n'). Los comandos de shell vis y cat pueden tratar caracteres no imprimibles, pero no espacios en blanco, por lo que esto no resolverá el problema del OP:

sed -i -e "1s/^/$(cat file_with_header.txt)/" file_to_be_prepended.txt

Falla debido a las nuevas líneas guión sustituto, que necesita ser precedido por un carácter de continuación de línea () y tal vez seguido por un&, para mantener el shell y sed felices, como esto SO answer

sed tiene un límite de tamaño de 40K para los comandos de búsqueda-reemplazo no globales (sin final /g después del patrón), por lo que probablemente evitaría los problemas de desbordamiento de búfer de awk que anonymous advirtió.

 2
Author: hobs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:55:05
sed -i -e "1s/^/new first line\n/" old_file.txt
 1
Author: shixilun,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-02-02 06:40:14

Con $ (command ) puede escribir la salida de un comando en una variable. Así que lo hice en tres comandos en una línea y sin archivo temporal.

originalContent=$(cat targetfile) && echo "text to prepend" > targetfile && echo "$originalContent" >> targetfile
 1
Author: JuSchu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-05 07:14:22

Si tiene un archivo grande (unos cientos de kilobytes en mi caso) y acceso a python, esto es mucho más rápido que cat soluciones de tuberías:

python -c 'f = "filename"; t = open(f).read(); open(f, "w").write("text to prepend " + t)'

 1
Author: crizCraig,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-12 21:42:25

Una solución con printf:

new_line='the line you want to add'
target_file='/file you/want to/write to'

printf "%s\n$(cat ${target_file})" "${new_line}" > "${target_file}"

También podrías hacer:

printf "${new_line}\n$(cat ${target_file})" > "${target_file}"

Pero en ese caso hay que estar seguro de que no hay ningún % en cualquier lugar, incluyendo el contenido del archivo de destino, ya que puede ser interpretado y arruinar sus resultados.

 1
Author: user137369,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-14 21:09:22

Puede usar la línea de comandos de perl:

perl -i -0777 -pe 's/^/my_header/' tmp

Donde-voy a crear un reemplazo en línea del archivo y -0777 sorberá todo el archivo y hará que ^ coincida solo con el principio. - pe imprimirá todas las líneas

O si my_header es un archivo:

perl -i -0777 -pe 's/^/`cat my_header`/e' tmp

Donde el /e permitirá una evaluación de código en la sustitución.

 1
Author: user5704481,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-07 00:15:05
current=`cat my_file` && echo 'my_string' > my_file && echo $current >> my_file

Donde "my_file" es el archivo al que se antepondrá "my_string".

 0
Author: vinyll,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-04-08 10:58:14

Me gusta el enfoque de@fluffle ed el mejor. Después de todo, los conmutadores de línea de comandos de cualquier herramienta frente a los comandos del editor de scripts son esencialmente lo mismo aquí; no ver una solución de editor de scripts "limpieza" siendo menor o lo que sea.

Aquí está mi one-liner anexado a .git/hooks/prepare-commit-msg para anteponer un archivo in-repo .gitmessage para confirmar mensajes:

echo -e "1r $PWD/.gitmessage\n.\nw" | ed -s "$1"

Ejemplo .gitmessage:

# Commit message formatting samples:
#       runlevels: boot +consolekit -zfs-fuse
#

Estoy haciendo 1r en lugar de 0r, porque eso dejará el vacío línea lista para escribir en la parte superior del archivo de la plantilla original. No pongas una línea vacía encima de tu .gitmessage entonces, terminarás con dos líneas vacías. -s suprime la salida de información de diagnóstico de la disfunción eréctil.

En relación con pasar por esto, yo descubrí que para los potenciadores de vim también es bueno tener: {[15]]}

[core]
        editor = vim -c ':normal gg'
 0
Author: lkraav,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:34:39

Variables, ftw?

NEWFILE=$(echo deb http://mirror.csesoc.unsw.edu.au/ubuntu/ $(lsb_release -cs) main universe restricted multiverse && cat /etc/apt/sources.list)
echo "$NEWFILE" | sudo tee /etc/apt/sources.list
 0
Author: Jayen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-17 10:20:38

Creo que esta es la variación más limpia de ed:

cat myheader | { echo '0a'; cat ; echo -e ".\nw";} | ed myfile

Como una función:

function prepend() { { echo '0a'; cat ; echo -e ".\nw";} | ed $1; }

cat myheader | prepend myfile
 0
Author: Dave Butler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-12-29 00:06:56

Si estás haciendo scripting en BASH, en realidad, solo puedes emitir:

cat - yourfile  /tmp/out && mv /tmp/out yourfile

Eso es en realidad en el Ejemplo Complejo que usted mismo publicó en su propia pregunta.

 0
Author: Tim Kennedy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-07-19 13:37:29

En mi humilde opinión no hay una solución de shell (y nunca será una) que funcione de manera consistente y confiable, cualquiera que sea el tamaño de los dos archivos myheader y myfile. La razón es que si desea hacerlo sin recurrir a un archivo temporal (y sin dejar que el shell recurra silenciosamente a un archivo temporal, por ejemplo, a través de construcciones como exec 3<>myfile, canalizando a tee, etc.

La solución "real" que está buscando necesita jugar con el sistema de archivos, por lo que no está disponible en el espacio de usuario y dependería de la plataforma: está pidiendo modificar el puntero del sistema de archivos en uso por myfile al valor actual del puntero del sistema de archivos para myheader y reemplazar en el sistema de archivos el EOF de myheader con un enlace encadenado a la dirección del sistema de archivos actual apuntada por myfile. Esto no es trivial y obviamente no puede ser hecho por un no-superusuario, y probablemente no por el superusuario tampoco... Juega con inodos, etc.

Puede más o menos falsificar esto usando dispositivos de bucle, sin embargo. Ver por ejemplo esto ASÍ hilo.

 0
Author: jaybee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:03:02