Jak wstawić zawartość pliku do innego pliku przed wzorem (znacznikiem)?


32

File1 zawartość:

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

File2 zawartość:

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

Po wykonaniu skryptu perl / shell File2treść powinna stać się:

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"

tzn wkleić zawartość File1w File2przed linią, która zawiera „pointer”.


1


Głosowałem już za pytaniem SO o zamknięcie tego powodu, nie ma już powodu, aby zamykać to pytanie. Btw, pytanie jest o wiele gorszej jakości na SO, więc logika nakazuje zamknąć to, a nie to.
Peter mówi, że przywróć Monikę

Odpowiedzi:


33

sed ma do tego funkcję i może dokonać modyfikacji bezpośrednio:

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

Ale to powoduje umieszczenie linii wskaźnika nad plikiem 1. Aby umieścić to poniżej, opóźnij wyjście liniowe:

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

8
Czy możesz wyjaśnić, co -e 1x -e '2,${x;p}' -e '${x;p}'robisz? Rozumiem, że wymieniasz rzeczy w buforze wzorów, a następnie drukujesz, ale nie wiem, co i dlaczego dodałeś opcję cichą -nna początku.
hdl

@ jfg956 Czy można po prostu zastąpić i usunąć część „Pointer” z oryginalnego pliku. Mogę to rozgryźć po raz drugi, ale czy można to zrobić za jednym razem?
Alexander Cska

17

Bez użycia sedlub awk...

Najpierw znajdź linię, na której znajduje się wzór:

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

Następnie użyj 3 poleceń, aby uzyskać pożądany wynik:

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

Ma to tę wadę, że dostęp do plików 3 razy file2, ale może być wyraźniejszy niż sedw awkroztworze.


10

awkczyni to dość łatwym.
Wstaw linię przed plikiem:

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

Aby wydrukować wewnętrzny plik po Pointerwierszu, wystarczy zmienić kolejność wzorców (musisz dodać średnik, aby uzyskać domyślną akcję) i możesz upuścić linezmienną:

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

I tylko dlatego, że nikt perljeszcze nie używał ,

# 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

działa, ale został usunięty wiersz zawierający wskaźnik
użytkownik1228191

podobnie jak wkleić zawartość pliku 1 do pliku 2 po tym „wskaźniku” zawierającym linię za pomocą awk
user1228191

@ user1228191 Naprawiono pierwszy, dodano drugi.
Kevin

Wydaje się, że wersja „perl” nie działa. system("cat innerfile")wysyła innerfiledo konsoli. Czy coś brakuje?
kaartic

komenda awk [gawk '/ <body> / {while (getline line <"$ HOME / bin / SrunScenario.style") {print line}} //' index.html> new_index.html] po prostu zapętla i wypisuje miliony linii . gawk V4.2.0 Czego tu brakuje?
JESii

7

Łatwa praca dla ed:

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

-r file1wczytuje podany plik za adresowaną linią, która w tym przypadku jest linią poprzedzającą dopasowanie pierwszej linii Pointer. To wstawi zawartość file2tylko raz, nawet jeśli Pointerwystępuje w wielu liniach. Jeśli chcesz wstawić go przed każdą pasującą linią, dodaj gflagę lobal:

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

Wymień ,psię wjeśli chcesz edytować plik w miejscu.


Zaakceptowana sedodpowiedź działa w większości przypadków, ale jeśli znacznik znajduje się w ostatnim wierszu, polecenie nie będzie działać zgodnie z oczekiwaniami: wstawi zawartość File1po znaczniku.
Początkowo próbowałem z:

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

co również działa dobrze (podobnie jak rmagia na końcu cyklu), ale ma ten sam problem, jeśli znacznik znajduje się na ostatniej linii (nie ma Nlinii zewnętrznej po ostatniej linii). Aby obejść ten problem, możesz dodać nowy wiersz do swoich danych wejściowych:

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)

Spowoduje to wstawienie file2treści przed każdą pasującą linią. Aby wstawić go tylko przed pierwszą pasującą linią, możesz użyć loop i po prostu przeciągnij nlinię ext, aż dojdziesz do końca pliku:

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

Dzięki tym sedrozwiązaniom tracisz możliwość edycji w miejscu (ale możesz przekierować do innego pliku).


6

Użyj pętli, aby odczytać linie w pliku 2. Jeśli znajdziesz linię zaczynającą się od Pointer, wydrukuj plik 1. Jest to pokazane poniżej:

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

4

Jest kilka sposobów na obejście tego w / sed. Jednym ze sposobów jest opóźniony odczyt, zgodnie z zaleceniami przyjętej odpowiedzi. Można to również napisać w następujący sposób:

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

... z nieco wyraźnym spoglądaniem w przyszłość zamiast z tyłu zaimplementowanym gdzie indziej z buforem wstrzymania. Które będą mieć ten sam problem z ostatniej linii, która @don_crissti zauważa jednak, bo N robi przyrost cykl linii i rpolecenia EAD jest stosowany przez numer linii.

Możesz obejść to:

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

Nie wszystkie seds zinterpretują -standardowe wejście, ale wielu tak. ( POSIX mówi, że sed powinien obsługiwać -standard-in, jeśli implementator chce -oznaczać standard-in ???)

Innym sposobem jest uporządkowanie dołączonej zawartości w kolejności. Istnieje inne polecenie, które planuje wyjście w taki sam sposób, jak rrobi to ead, i sedzastosuje je i read w kolejności, w jakiej są skrypty. Jest jednak trochę bardziej zaangażowany - wymaga użycia jednego, sedaby adopasować Pointerdopasowanie do wyniku innego sedw swoim skrypcie.

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

Zasadniczo pierwszy sedpisze drugi sedskrypt, który drugi sedodczytuje na standardowym wejściu (może ...) i stosuje z kolei. Pierwszy seddziała tylko przy pierwszym dopasowaniu dla Pointerznalezionego, a następnie qwprowadza dane wejściowe. Jego zadaniem jest ...

  1. s/[]^$&\./*[]/\\&/g;H
    • Upewnij się, że wszystkie znaki wzorcowe są bezpiecznie odwrócone ukośnikiem, ponieważ drugi sedbędzie musiał zinterpretować każdy fragment, który czyta dosłownie, aby go poprawnie. Gdy to zrobisz, umieść kopię w Hstarej przestrzeni.
  2. s|.*|/&/!p;//!d|p; x
    • Powiedz drugiemu, sedaby pzrewidował każdą linię wprowadzania, !ale tę, /&/którą właśnie zabezpiecziliśmy; a następnie dusunąć to samo. pzrewiduj polecenia po drugim sed, a następnie xzmień hstary i bufor buforów wzorców, aby działały na naszej zapisanej kopii.
  3. s|.|r file1&a\\&|p;q
    • Jedynym char, z którym pracujemy tutaj, jest \newline, ponieważ sedbędzie poprzedzał jeden, gdy staraliśmy się Ho linię wcześniej. Więc wstawiamy polecenie r file1i podążamy za nim z naszą \newline, a następnie polecenie a\\dla append, a następnie także \newline. Cała reszta naszej Hlinii eld jest zgodna z ostatnią \nlinią ewline.

Skrypt, który pierwszy pisze, wygląda mniej więcej tak:

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

Zasadniczo drugi sedwypisze każdą linię, ale ten pierwszy sedustawi ją na append. Dla tej konkretnej linii zaplanowano dwa opóźnione zapisy do standardowego wyjścia - pierwszy to ead, a drugi to kopia linii, którą chcemy po nim. Pierwsze doktorowanie nie jest nawet konieczne w tym przypadku (patrz? Żadnych odwrotnych ukośników), ale ważne jest, aby bezpiecznie uciec w sposób, w jaki tu robię, za każdym razem, gdy dopasowanie wzorca zostanie zmienione jako wejście.rfile1sed

W każdym razie, więc ... jest kilka sposobów.


2

W AWK jest to dość proste:

Plik1 do pliku2 przed wzorcem = „Wskaźnik”

Najpierw załaduj zawartość pliku File1 do zmiennej

f1="$(<File1)"

następnie włóż

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

(Lub, jeśli chcesz wstawić File1 po „Pointer”)

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

2

mój preferowany sposób: szablony .

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

Spowoduje to zastąpienie każdego changeme wystąpienie w origfile z treścią file2insert . Usuń ostatnią g z sed, aby zastąpić tylko pierwsze wystąpienie ZMIANY .


Jak korzystać $xz pierwszego polecenia, jeśli jest ono zdefiniowane tylko w drugim poleceniu?
Totor

„$ x” w pierwszym poleceniu jest tylko symbolem zastępczym, który ma być oceniany przez envsubst w drugim poleceniu. Przy pojedynczych cudzysłowach skryptu sed $ x nie jest obliczany przez twoją powłokę.
nrc

2

[Wstaw zawartość pliku do innego pliku PRZED wzorcem]

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

[Po wzorze]

sed -i '/PATTERN/r file1' file2

Ndziała ładnie, ale nie, jeśli WZORZEC pasuje do ostatniego wiersza wejścia
Sundeep

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.