sed: jak zastąpić linię, jeśli znaleziono, lub dołączyć do końca pliku, jeśli nie znaleziono?


33

W przypadku pojedynczego pliku wejściowego zawierającego tylko komentarze (zaczynające się od #) i ZMIENNE = wiersze wartości, czy można zastąpić wartość jednej zmiennej, jeśli zostanie znaleziona, a w przeciwnym razie dołączyć parę na końcu pliku, jeśli nie zostanie znaleziona?

Moja obecna metoda polega na usunięciu jej w pierwszym przejściu, a następnie dołączeniu jej na końcu pliku w drugim przejściu, ale ta metoda mierzy kolejność wierszy (i są to również dwa różne polecenia):

sed -r "/^FOOBAR=.*$/d"      -i samefile &&
sed -r "$ a\FOOBAR=newvalue" -i samefile

Czy można to zrobić, tj. utrzymywanie kolejności linii, w jednej linii sed? Jeśli zrobi to jakiś inny program narzędziowy (awk, ...), przejmę to.

Odpowiedzi:


29

W rzeczywistości jest to bardzo proste sed: jeśli wiersz pasuje, po prostu skopiuj go do hstarej spacji, a następnie swstaw wartość.
Na pierwszej $linii xzmień przestrzeń wstrzymania i przestrzeń wzoru, a następnie sprawdź, czy ta ostatnia jest pusta. Jeśli nie jest pusty, oznacza to, że podstawienie zostało już wykonane, więc nic nie można zrobić. Jeśli jest pusty, oznacza to, że nie znaleziono dopasowania, zamień przestrzeń wzorców na żądaną wartość zmiennej = wartość, a następnie dołącz do bieżącej linii w buforze wstrzymania. Na koniec xzmień ponownie:

sed '/^FOOBAR=/{h;s/=.*/=newvalue/};${x;/^$/{s//FOOBAR=newvalue/;H};x}' infile

Powyżej jest gnu sedskładnia. Przenośny:

sed '/^FOOBAR=/{
h
s/=.*/=newvalue/
}
${
x
/^$/{
s//FOOBAR=newvalue/
H
}
x
}' infile

1
Jak by to wyglądało, gdyby newvaluebyło przechowywane w zmiennej?
user1810087,

sed "/^${varName}=/{h;s/=.*/=${varValue}/};\${x;/^$/{s//${varName}=${varValue}/;H};x}" ${VARFILE}ze zmiennymi, podwójnymi cudzysłowami, aby umożliwić podstawienie zmiennych i unikanie $$$varNamesed "/^${varName}=/{h;s/=.*/=${!varName}/};\${x;/^$/{s//${varName}=${!varName}/;H};x}" ${VARFILE}
znaku,

1
To rozwiązanie nie jest „proste” lol. Ale to działa. strony podręcznika sed nie są najłatwiejszymi rzeczami do przeszukania, byłoby naprawdę pomocne, gdybyś mógł szczegółowo opisać efekt każdej sekcji wyrażenia :)
user2066480

18

Można to prawdopodobnie skrócić. To nie jest pojedyncza komenda sed, a także używa grep, ale wydaje się, że jest to zasadniczo to, czego chcesz. Jest to pojedynczy wiersz i służy do edycji pliku w miejscu (bez plików tymczasowych).

grep -q "^FOOBAR=" file && sed "s/^FOOBAR=.*/FOOBAR=newvalue/" -i file || 
    sed "$ a\FOOBAR=newvalue" -i file

11

Oto prostsze sedpodejście, ponieważ nie jest mi sed hold spacełatwo pracować. Jeśli czujesz się komfortowo hold space, użycie metody don_crissti daje dodatkową możliwość zachowania czegokolwiek z istniejącej linii, ale zwykle jest to bardzo rzadkie.

W tym podejściu wystarczy wydrukować wszystko oprócz linii, którą chcesz upuścić, a następnie na końcu dołączyć zamiennik.

sed -n -e '/^FOOBAR=/!p' -e '$aFOOBAR=newvalue' infile

1
Ta wersja jest bardzo dobra, jeśli planujesz zaktualizować plik, który może mieć istniejącą, ale nieaktualną wartość.
guillem

3

Opierając się na innych odpowiedziach, jeśli to, co chcesz zrobić, to zastąpić wartość zmiennej, jeśli ta zmienna jest obecna w pliku, i dołączyć ją na końcu pliku, jeśli nie jest (co nie robi twoich sedpoleceń) mógłbym spróbować:

perl -ne '$c=1 if s/^FOOBAR=.*$/FOOBAR=newvalue/;  
             print; 
             END{print "FOBAR=newvalue" unless $c==1}' file > tmpfile && 
mv tmpfile file

1

W awk jest to trochę łatwiejsze, chociaż „edycja na miejscu” nie jest automatyczna:

awk -v varname="FOOBAR" -v newval="newvalue" '
    BEGIN {FS = OFS = "="}
    $1 == varname {$2 = newval; found = 1}
    {print}
    END {if (! found) {print varname, newval}}
' file > tempfile &&
mv tempfile file

1

Po prostu użyj grepi, echoaby utworzyć pusty rekord:

grep -q '^FOOBAR=' somefile || echo 'FOOBAR=VALUE' >> somefile
sed -i 's/FOOBAR=.*$/FOOBAR=VALUE/' somefile 

Każda linia ucieka z kodem błędu zero.


Tracisz kolejność wierszy, dodając dodatkowe polecenia grepiecho
BlakBat

Rzeczywiście, jeśli wstawisz nowe elementy na końcu pliku, ale kontynuuj zamawianie, jeśli zastąpisz starą wartość.
MUY Belgia,

0

Właściwie działa z następującymi: sed -i '/^FOOBAR=/{h;s/=.*/=newvalue/};${x;/^$/{s//FOOBAR=newvalue/;H};x}' infile W odpowiedzi wybrano -i brakuje.


3
To jest naprawdę komentarz, a nie odpowiedź na pierwotne pytanie. Aby skrytykować lub poprosić autora o wyjaśnienie, zostaw komentarz pod jego postem - zawsze możesz komentować własne posty, a gdy będziesz mieć odpowiednią reputację , będziesz mógł komentować każdy post . Proszę przeczytać Dlaczego potrzebuję 50 reputacji, aby móc komentować? Co mogę zamiast tego zrobić?
DavidPostill

-1

aby to zrobić za pomocą Perla i na miejscu, działa to dobrze dla mnie:

grep ^FOOBAR= my.file && perl -i -ple "s/^FOOBAR=.+/FOOBAR=newvalue/g" my.file || echo FOOBAR=newvalue >> my.file

2
Ta odpowiedź jest zła na wiele sposobów! grepa echoi perlzamiast robić wszystko perl? Czy piszesz do pliku ( >> my.file) podczas jego czytania ( grep my.file)?
vladr
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.