Come inserire il contenuto di un file in un altro file prima di un pattern (marker)?


32

File1 contenuto:

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

File2 contenuto:

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

Dopo l'esecuzione dello script perl / shell, il File2contenuto dovrebbe diventare:

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"

cioè incollare il contenuto di File1in File2prima della riga che contiene "Puntatore".


1
Anche chiesto a StackOverflow
glenn jackman,


Ho già votato la domanda SO per la chiusura su questo motivo, non c'è più motivo di chiudere questa domanda. A proposito, la domanda è molto peggiore per la SO, quindi la logica impone di chiudere questo e non questo.
Peter dice di reintegrare Monica il

Risposte:


33

sed ha una funzione per quello e può fare la modifica in linea:

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

Ma questo mette la linea del puntatore sopra il file1. Per metterlo sotto, ritardare l'uscita della linea:

sed -n -i -e '/Pointer/r file1' -e 1x -e '2,${x;p}' -e '${x;p}' file2 

8
Potresti spiegare cosa -e 1x -e '2,${x;p}' -e '${x;p}'fare? Capisco che si scambiano elementi nel buffer dei pattern e poi li stampi ma non so cosa né perché hai aggiunto l'opzione quiet -nall'inizio.
hdl,

@ jfg956 È possibile semplicemente sostituire ed eliminare la parte 'Puntatore' dal file originale. Questo posso capire con una seconda spazzata di sed, ma è possibile farlo in una corsa?
Alexander Cska,

17

Senza usare sedo awk...

Innanzitutto, trova la linea su cui si trova il tuo modello:

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

Quindi, utilizzare 3 comandi per generare il risultato desiderato:

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

Questo ha lo svantaggio di accesso 3 volte la lima file2, ma potrebbe essere più chiaro di una seddi awksoluzione.


10

awkrende abbastanza facile.
Inserisci la riga prima del file:

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

Per fare in modo che il file interno venga stampato dopo la Pointerriga, basta cambiare l'ordine dei motivi (è necessario aggiungere un punto e virgola per ottenere l'azione predefinita), quindi è possibile eliminare la linevariabile:

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

E solo perché nessuno ha perlancora usato ,

# 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

funziona, ma viene rimossa la riga contenente il puntatore
user1228191,

allo stesso modo, come incollare il contenuto del file 1 nel file 2 dopo quel "Puntatore" contenente la riga usando awk
user1228191

@ user1228191 Risolto il primo, aggiunto il secondo.
Kevin,

La versione 'perl' non sembra funzionare. system("cat innerfile")invia l'output innerfilealla console. Mi sto perdendo qualcosa?
kaartic,

comando awk [gawk '/ <body> / {while (linea getline <"$ HOME / bin / SrunScenario.style") {linea di stampa}} //' index.html> new_index.html] semplicemente scorre e stampa milioni di linee . gawk V4.2.0 Cosa mi manca qui?
GESii,

7

Un lavoro facile per ed:

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

-r file1legge nel file specificato dopo la riga indirizzata, che in questo caso è la riga precedente alla prima riga corrispondente Pointer. Quindi questo inserirà il contenuto di file2una sola volta anche se si Pointerverifica su più righe. Se vuoi inserirlo prima di ogni riga corrispondente aggiungi global flag:

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

Sostituire ,pcon wse si desidera modificare il file sul posto.


La accettato sedrisposta funziona per la maggior parte dei casi, ma se il marcatore è sull'ultima riga, il comando non funziona come previsto: si inserirà il contenuto del File1dopo il marcatore.
Inizialmente ho provato con:

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

che funziona anche bene (come rfarà la sua magia alla fine del ciclo) ma ha lo stesso problema se il marker si trova sull'ultima riga (non c'è alcuna Nriga ext dopo l'ultima riga). Per ovviare a questo, è possibile aggiungere una nuova riga al proprio input:

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)

Ciò inserirà il file2contenuto prima di ogni riga corrispondente. Per inserirlo solo prima della prima riga corrispondente, è possibile utilizzare un loop e tirare la nriga ext fino a quando non si arriva alla fine del file:

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

Con queste sedsoluzioni perdi la possibilità di modificare sul posto (ma puoi reindirizzare a un altro file).


6

Utilizzare un ciclo per leggere le righe in file2. Se trovi una riga che inizia con Pointer, stampa il file1. Questo è mostrato di seguito:

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

4

Ci sono alcuni modi per procedere con questo sed. Un modo è una lettura ritardata come raccomandato nella risposta accettata. Potrebbe anche essere scritto come:

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

... con un po 'di look-forward esplicito anziché il look-behind implementato altrove con il buffer di conservazione. Che inevitabilmente hanno lo stesso problema con l'ultima riga che le note @don_crissti, però, perché N fa di incremento del ciclo di linea e il rcomando di EAD è applicata in base al numero di linea.

Puoi aggirarlo:

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

Non tutti sedinterpreteranno il -significato di input standard, ma molti lo fanno. ( POSIX dice che sed dovrebbe supportare -significare standard-in se l'implementatore vuole -dire standard-in ???)

Un altro modo è gestire il contenuto allegato in ordine. C'è un altro comando che pianifica l' output nello stesso modo in rcui lo fa e sedlo applicherà e lo rfarà nell'ordine in cui sono stati scritti. Tuttavia è un po 'più coinvolto: implica l'utilizzo di uno sedper asospendere la Pointercorrispondenza all'output di un altro sednel suo 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

Quindi sostanzialmente il primo sedscrive il secondo seduno script, che il secondo sedlegge sullo standard input (forse ...) e applica a sua volta. Il primo sedfunziona solo sulla prima corrispondenza per Pointerfound, e successivamente utilizza l' qinput. Il suo compito è ...

  1. s/[]^$&\./*[]/\\&/g;H
    • Assicurati che tutti i caratteri del modello siano salvati in modo sicuro con il backslash perché il secondo seddovrà interpretare ogni bit che legge alla lettera per farlo bene. Una volta fatto, mettine una copia nel Hvecchio spazio.
  2. s|.*|/&/!p;//!d|p; x
    • Di 'al secondo seddi pstampare tutte le linee di input !tranne /&/quella che abbiamo appena protetto dal modello; e poi per deliminare tutti uguali. pstampare i comandi al secondo sed, quindi xcambiare i hbuffer vecchi e di pattern per lavorare sulla nostra copia salvata.
  3. s|.|r file1&a\\&|p;q
    • L'unico carattere con cui lavoriamo qui è una \newline perché sedne abbiamo anteposto uno prima Hdella riga precedente. Quindi inseriamo il comando r file1e lo seguiamo con la nostra \newline quindi il comando a\\per append seguito anche da una \newline. Tutto il resto della nostra Hlinea eld segue l'ultima \newline.

Lo script che il primo scrive è simile al seguente:

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

Fondamentalmente il secondo sedstamperà ogni riga, ma il primo lo sedimposta su append. Per quella particolare riga sono programmate due scritture ritardate in uscita standard : la prima è la prima e la seconda è una copia della riga che vogliamo dopo. Il primo dottorato non è nemmeno necessario in questo caso (vedi? Nessuna barra rovesciata) ma è importante fuggire in modo sicuro come faccio qui ogni volta che un pattern match viene riproposto come input.rfile1sed

Comunque, quindi ... ci sono alcuni modi.


2

Questo è abbastanza semplice con AWK:

File1 in File2 prima di pattern = "Puntatore"

Prima carica il contenuto di File1 in una variabile

f1="$(<File1)"

quindi eseguire l'inserimento

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

(Oppure, se si desidera inserire File1 dopo "Puntatore")

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

2

il mio modo preferito: il templating .

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

Questo sostituirà ogni changeme occorrenza in origfile con il contenuto di file2insert . Rimuovi l'ultimo g da sed per sostituire solo la prima occorrenza di CHANGEME .


Come si può usare $xnel primo comando, quando è definito solo nel secondo comando?
Totor,

"$ x" nel primo comando è solo un segnaposto che deve essere valutato da envsubst nel secondo comando. Con virgolette singole di sed script, $ x non viene valutato dalla shell.
nrc,

2

[Inserire il contenuto del file in un altro file PRIMA del modello]

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

[Dopo il modello]

sed -i '/PATTERN/r file1' file2

Nfunziona bene, ma non se il modello corrisponde all'ultima riga di input
Sundeep

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.