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.

BlakBat
źródło

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
don_crissti
źródło
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
snapshoe
źródło
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
haridsv
źródło
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
terdon
źródło
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
Glenn Jackman
źródło
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.

MUY Belgium
źródło
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.

Yogesh Kamat
źródło
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
antyweiss
źródło
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