¿Cómo insertar el contenido de un archivo en otro archivo antes de un patrón (marcador)?

32

File1 contenido:

line1-file1      "1" 
line2-file1      "2"
line3-file1      "3" 
line4-file1      "4" 

File2 contenido:

line1-file2     "25"  
line2-file2     "24"  
Pointer-file2   "23"  
line4-file2     "22" 
line5-file2     "21"

Después de la ejecución del script perl / shell, el File2contenido debería convertirse en:

line1-file2     "25"  
line2-file2     "24" 
line1-file1      "1" 
line2-file1      "2"
line3-file1      "3" 
line4-file1      "4" 
Pointer-file2   "23" 
line4-file2     "22" 
line5-file2     "21"

es decir, pegar el contenido de File1en File2antes de la línea que contiene "Pointer".

usuario1228191
fuente
1
También preguntado en StackOverflow
Glenn Jackman
Ya voté la pregunta SO para el cierre por este motivo, no hay más motivos para cerrar esta pregunta. Por cierto, la cuestión es una calidad mucho peor en el SO, por lo que la lógica dicta cerrar eso y no esto.
Peter dice reinstalar a Mónica el

Respuestas:

33

sed tiene una función para eso y puede hacer la modificación en línea:

sed -i -e '/Pointer/r file1' file2

Pero esto pone su línea de puntero sobre el archivo1. Para ponerlo a continuación, retrase la salida de línea:

sed -n -i -e '/Pointer/r file1' -e 1x -e '2,${x;p}' -e '${x;p}' file2 
jfg956
fuente
8
¿Podrías explicar qué -e 1x -e '2,${x;p}' -e '${x;p}'hacer? Entiendo que intercambias cosas en el búfer de patrones y luego las imprimes, pero no sé qué ni por qué agregaste la opción silenciosa -nal principio.
hdl
@ jfg956 ¿Es posible sustituir y eliminar la parte 'Puntero' del archivo original? Esto lo puedo resolver con un segundo barrido de sed, pero ¿es posible hacerlo de una vez?
Alexander Cska
17

Sin usar sedo awk...

Primero, encuentre la línea en la que se encuentra su patrón:

line=$(grep -n 'Pointer' file2 | cut -d ":" -f 1)

Luego, use 3 comandos para generar el resultado deseado:

{ head -n $(($line-1)) file2; cat file1; tail -n +$line file2; } > new_file

Esto tiene el inconveniente de acceder a 3 veces el archivo file2, pero podría ser más claro que una sedde awksolución.

jfg956
fuente
10

awkhace que esto sea bastante fácil.
Inserte la línea antes del archivo:

awk '/Pointer/{while(getline line<"innerfile"){print line}} //' outerfile >tmp
mv tmp outerfile

Para hacer que el archivo interno se imprima después de la Pointerlínea, simplemente cambie el orden de los patrones (necesita agregar un punto y coma para obtener la acción predeterminada), y puede soltar la linevariable:

awk '//; /Pointer/{while(getline<"innerfile"){print}}' outerfile >tmp
mv tmp outerfile

Y solo porque nadie lo ha usado perltodavía,

# insert file before line
perl -e 'while(<>){if($_=~/Pointer/){system("cat innerfile")};print}' outerfile

# after line
perl -e 'while(<>){print;if($_=~/Pointer/){system("cat innerfile")}}' outerfile
Kevin
fuente
funciona, pero se elimina la línea que contiene el puntero
user1228191
del mismo modo, cómo pegar el contenido del archivo 1 en el archivo 2 después de esa línea que contiene "puntero" usando awk
user1228191
@ user1228191 Se arregló el primero y se agregó el segundo.
Kevin
La versión 'perl' no parece estar funcionando. system("cat innerfile")da salida innerfilea la consola. ¿Me estoy perdiendo de algo?
kaartic
El comando awk [gawk '/ <body> / {while (getline line <"$ HOME / bin / SrunScenario.style") {print line}} //' index.html> new_index.html] simplemente repite e imprime millones de líneas . gawk V4.2.0 ¿Qué me estoy perdiendo aquí?
JESii
7

Un trabajo fácil para ed:

ed -s file1 <<IN
/Pointer/-r file2
,p
q
IN

-r file1lee en el archivo especificado después de la línea direccionada, que en este caso es la línea anterior a la primera coincidencia de línea Pointer. Entonces esto insertará el contenido de file2una sola vez, incluso si Pointerocurre en varias líneas. Si desea insertarlo antes de cada línea coincidente, agregue la gbandera lobal:

ed -s file1 <<IN
g/Pointer/-r file2
,p
q
IN

Reemplace ,pcon wsi desea editar el archivo en el lugar.


La sedrespuesta aceptada funciona para la mayoría de los casos, pero si el marcador está en la última línea, el comando no funcionará como se esperaba: insertará el contenido File1después del marcador.
Inicialmente he intentado con:

sed '/Pointer/{r file1
N}' file2

que también funciona bien (como rlo hará su magia al final del ciclo) pero tiene el mismo problema si el marcador está en la última línea (no hay una Nlínea ext después de la última línea). Para evitar eso, puede agregar una nueva línea a su entrada:

sed '/Pointer/{              # like the first one, but this time even if the
r file1                      # marker is on the last line in File2 it
N                            # will be on the second to last line in
}                            # the combined input so N will always work;
${                           # on the last line of input: if the line is
/^$/!{                       # not empty, it means the marker was on the last
s/\n$//                      # line in File2 so the final empty line in the
}                            # input was pulled i\n: remove the latter;
//d                          # if the line is empty, delete it
}' file2 <(printf %s\\n)

Esto insertará file2contenido antes de cada línea coincidente. Para insertarlo solo antes de la primera línea coincidente, puede usar un loop y simplemente jalar la nlínea de extensión hasta llegar al final del archivo:

sed '/Pointer/{
r file2
N
:l
$!n
$!bl
}
${
/^$/!{
s/\n$//
}
//d
}' file1 <(printf %s\\n)

Con estas sedsoluciones, pierde la capacidad de editar en el lugar (pero puede redirigir a otro archivo).

don_crissti
fuente
6

Use un bucle para leer las líneas en el archivo2. Si encuentra una línea que comienza con Pointer, imprima el archivo1. Esto se muestra a continuación:

#!/bin/bash
while IFS= read -r line
do
    if [[ "$line" =~ ^Pointer.*$ ]]
    then
        cat file1
    fi
    echo "$line"
done < file2
dogbane
fuente
4

Hay algunas maneras de hacer esto con / sed. Una forma es una lectura retrasada como se recomienda en la respuesta aceptada. También podría escribirse como:

sed -e '$!N;P;/\nPointer/r file1' -e D file2

... con un poco de anticipación explícita en lugar de la retrospectiva implementada en otros lugares con el búfer de retención. Sin embargo, eso inevitablemente tendrá el mismo problema con la última línea que @don_crissti observa, ya N que incrementa el ciclo de la línea y el rcomando ead se aplica por número de línea.

Puedes evitarlo:

echo | sed -e '$d;N;P;/\nPointer/r file1' -e D file2 -

No todos los seds interpretarán el -significado de entrada estándar, pero muchos lo hacen. ( POSIX dice que sed debería admitir la entrada -estándar estándar si el implementador quiere -decir entrada estándar ???)

Otra forma es manejar el contenido agregado en orden. Hay otro comando que programa la salida de la misma manera que read, y la sedaplicará y read en el orden en que están escritas. Sin embargo, es un poco más complicado: implica usar uno sedpara aagregar la Pointercoincidencia a la salida de otro seden su script.

sed '   /Pointer/!d                  #only operate on first match
        s/[]^$&\./*[]/\\&/g;H        #escape all metachars, Hold
        s|.*|/&/!p;//!d|p;g          #print commands, exchange
        s|.|r file1&a\\&|;q' file2|  #more commands, quit
        sed -nf - file2              #same input file

Entonces, básicamente, el primero sedescribe el segundo sedun script, que el segundo sedlee en entrada estándar (tal vez ...) y se aplica a su vez. El primero sedsolo funciona en la primera coincidencia para Pointerencontrado, y luego qingresa. Su trabajo es ...

  1. s/[]^$&\./*[]/\\&/g;H
    • Asegúrese de que todos los caracteres del patrón tengan una barra invertida sin escape porque el segundo sedtendrá que interpretar cada bit que lee literalmente para hacerlo bien. Una vez hecho esto, coloque una copia en el Hespacio antiguo.
  2. s|.*|/&/!p;//!d|p; x
    • Dígale al segundo sedque pborre cada línea de entrada, !excepto la /&/que acabamos de guardar con patrones; y luego delegir todo lo mismo. pborre los comandos en el segundo sed, luego xcambie los hbúferes antiguos y de patrones para que funcionen en nuestra copia guardada.
  3. s|.|r file1&a\\&|p;q
    • El único \npersonaje con el que trabajamos aquí es un ewline porque sedhabrá antepuesto uno cuando Heludimos la línea antes. Entonces, insertamos el comando r file1y lo seguimos con nuestra línea \nelectrónica, luego el comando a\\para append seguido también por una línea \nelectrónica. Todo el resto de nuestra Hlínea de campo sigue esa última \nlínea.

El guión que escribe el primero se parece a esto:

/Pointer-file2   "23"/!p;//!d
r file1
a\
Pointer-file2   "23"

Básicamente, el segundo sedimprimirá cada línea, pero la primera la sedconfigurará para apasar. Por esa particular línea de dos escrituras con retraso a la salida estándar están programados - el primero es el read de file1y el segundo es una copia de la línea que queremos después de ella. El primer sedcuidado no es necesario en este caso (¿ve? No hay barras invertidas) pero es importante escapar de manera segura como lo hago aquí cada vez que una coincidencia de patrón se reutiliza como entrada.

De todos modos, entonces ... hay algunas maneras.

mikeserv
fuente
2

Esto es bastante simple con AWK:

File1 en File2 antes de pattern = "Pointer"

Primero cargue el contenido de File1 en una variable

f1="$(<File1)"

luego hacer la inserción

awk -vf1="$f1" '/Pointer/{print f1;print;next}1' file2

(O, si desea insertar File1 después de "Puntero")

awk -vf1="$f1" '/Pointer/{print;print f1;next}1' file2
Amos Folarin
fuente
2

Mi forma preferida: plantillas .

sed 's/CHANGEME/$x/g' origfile | x="$(<file2insert)" envsubst '$x' > newfile

Esto reemplazará todos los changeme aparición en origfile con el contenido de file2insert . Retire el último g de sed para reemplazar solo la primera aparición de CAMBIO .

nrc
fuente
¿Cómo se puede usar $xen su primer comando, cuando solo se define en su segundo comando?
Totor
el "$ x" en el primer comando es solo un marcador de posición para ser evaluado por envsubst en el segundo comando. Con comillas simples de script sed, $ x no es evaluado por su shell.
nrc
2

[Insertar el contenido del archivo en otro archivo ANTES del patrón]

sed -i '/PATTERN/r file1' -e //N file2

[Después del patrón]

sed -i '/PATTERN/r file1' file2
Jim.Y
fuente
Nfunciona bien, pero no si el PATRÓN coincide con la última línea de entrada
Sundeep
0
 awk '/Pointer/{system("cat File1")}1' File2
dedowsdi
fuente