Dołączanie wiersza do pliku tylko wtedy, gdy jeszcze nie istnieje


157

Muszę dodać następujący wiersz na końcu pliku konfiguracyjnego:

include "/configs/projectname.conf"

do pliku o nazwie lighttpd.conf

Chcę użyć seddo tego, ale nie wiem, jak to zrobić.

Jak wstawiłbym go tylko wtedy, gdy linia jeszcze nie istnieje?


Jeśli próbujesz edytować plik ini, narzędzie crudinimoże być dobrą opcją (ale jeszcze nie dla lighthttpd)
rubo77

Odpowiedzi:


291

Po prostu nie komplikuj :)

grep + echo powinno wystarczyć:

grep -qxF 'include "/configs/projectname.conf"' foo.bar || echo 'include "/configs/projectname.conf"' >> foo.bar

Edycja: włączone sugestie @cerin i @ thijs-wouters .


2
Dodałbym przełącznik -q do grep, aby stłumić wyjście: grep -vq ...
Dave Kirby

1
FYI, użycie -v i && nie wydaje się działać, podczas gdy || bez -v działa.
bPizzi

3
Działa to tylko w przypadku bardzo prostych linii. W przeciwnym razie grep interpretuje większość znaków innych niż alfa w twoim wierszu jako wzorce, powodując, że nie wykrywa wiersza w pliku.
Cerin,

2
Piękne rozwiązanie. Oczywiście działa to również w przypadku wyzwalania bardziej skomplikowanych wyrażeń. Mój używa the echodo wyzwolenia cata wielowierszowego heredoc do pliku konfiguracyjnego.
Eric L.

2
Dodaj, -saby zignorować błędy, gdy plik nie istnieje, tworząc nowy plik zawierający tylko tę linię.
Frank

78

Byłoby to czyste, czytelne i wielokrotnego użytku rozwiązanie wykorzystujące grepi echododające wiersz do pliku tylko wtedy, gdy jeszcze nie istnieje:

LINE='include "/configs/projectname.conf"'
FILE='lighttpd.conf'
grep -qF -- "$LINE" "$FILE" || echo "$LINE" >> "$FILE"

Jeśli chcesz dopasować całą linię, użyj grep -xqF

Dodaj, -saby zignorować błędy, gdy plik nie istnieje, tworząc nowy plik zawierający tylko tę linię.


5
Jeśli jest to plik, w którym potrzebujesz uprawnień roota:sudo grep -qF "$LINE" "$FILE" || echo "$LINE" | sudo tee -a "$FILE"
nebffa

To faktycznie nie działa, gdy wiersz zaczyna się od -.
hyperknot

@zsero: dobra uwaga! Dodałem --polecenie grep, więc nie będzie już interpretować $ LINE zaczynającego się od myślnika jako opcji.
rubo77

13

Oto sedwersja:

sed -e '\|include "/configs/projectname.conf"|h; ${x;s/incl//;{g;t};a\' -e 'include "/configs/projectname.conf"' -e '}' file

Jeśli twój ciąg jest w zmiennej:

string='include "/configs/projectname.conf"'
sed -e "\|$string|h; \${x;s|$string||;{g;t};a\\" -e "$string" -e "}" file

W przypadku polecenia, które zastępuje częściowy ciąg pełnym / zaktualizowanym wpisem w pliku konfiguracyjnym lub dodaje wiersz w razie potrzeby:sed -i -e '\|session.*pam_mkhomedir.so|h; ${x;s/mkhomedir//;{g;tF};a\' -e 'session\trequired\tpam_mkhomedir.so umask=0022' -e '};:F;s/.*mkhomedir.*/session\trequired\tpam_mkhomedir.so umask=0022/g;' /etc/pam.d/common-session
bgStack15

Świetnie, bardzo mi to pomogło +1. czy mógłbyś, proszę, wyjaśnić swoje wyrażenie sed?
Rahul

Chciałbym zobaczyć objaśnienie tego rozwiązania z adnotacjami. Używam od sedlat, ale głównie po prostu spolecenie. Zamiany holdi patternmiejsca są poza mną.
północne

11

Jeśli piszesz do chronionego pliku, odpowiedzi @drAlberT i @ rubo77 mogą nie działać dla ciebie, ponieważ nie można sudo >>. Podobnie proste rozwiązanie zatem byłoby użycie tee --append(lub na MacOS tee -a)

LINE='include "/configs/projectname.conf"'
FILE=lighttpd.conf
grep -qF "$LINE" "$FILE"  || echo "$LINE" | sudo tee --append "$FILE"

6

Jeśli pewnego dnia ktoś inny będzie musiał traktować ten kod jako „stary kod”, osoba ta będzie wdzięczna, jeśli napiszesz mniej egzotyczny kod, taki jak

grep -q -F 'include "/configs/projectname.conf"' lighttpd.conf
if [ $? -ne 0 ]; then
  echo 'include "/configs/projectname.conf"' >> lighttpd.conf
fi

1
Przepraszamy, jeśli nie znasz powłoki, przejdź do administrowania systemem Windows. Rozwiązania wykorzystujące && i || to normalna składnia powłoki i powinna być zrozumiała dla każdego kompetentnego administratora. W ogóle nie ma tam nic ezoterycznego.
Graham Nicholls

3
Dzięki za komentarz Graham! Tak, to normalna składnia powłoki. I tak, każdy kompetentny administrator powinien to zrozumieć. Jednak działa jako taki w oparciu o efekt uboczny algorytmu oceny wyrażeń w powłoce. Możesz więc nazwać to wszystko, co lubisz, z wyjątkiem prostych - co było moim pierwotnym celem.
Marcelo Ventura


6

innym rozwiązaniem seda jest zawsze dołączanie go do ostatniej linii i usuwanie wcześniej istniejącego.

sed -e '$a\' -e '<your-entry>' -e "/<your-entry-properly-escaped>/d"

„Właściwie zmieniona ucieczka” oznacza umieszczenie wyrażenia regularnego, które pasuje do twojego wpisu, tj. wymknięcie się wszystkim kontrolkom wyrażenia regularnego z twojego aktualnego wpisu, tj. umieszczenie ukośnika odwrotnego przed ^ $ / *? + ().

to może się nie powieść w ostatnim wierszu twojego pliku lub jeśli nie ma wiszącego nowego wiersza, nie jestem pewien, ale można sobie z tym poradzić przez sprytne rozgałęzienie ...


1
Ładny, jednoliniowy, krótki i łatwy do odczytania. Działa świetnie przy aktualizowaniu etc / hosts:sed -i.bak -e '$a\' -e "$NEW_IP\t\t$HOST.domain\t$HOST" -e "/.*$HOST/d" /etc/hosts
Noam Manos

Mniejsza wersja:sed -i -e '/<entry>/d; $a <entry>'
Jérôme Pouiller

@Jezz Myślę, że to się nie powiedzie, jeśli wpis znajduje się już na końcu pliku, ponieważ dpolecenie zrestartuje program seda i tym samym pominie append, jeśli usunięcie nastąpiło w ostatniej linii.
Robin479

@ Robin479 Damned, append muszą być wykonywane przed dsuĹ: sed -i -e '$a<entry>' -e '/<entry>/d'. To prawie to samo, co Twoja pierwotna odpowiedź.
Jérôme Pouiller

3

użyj awk

awk 'FNR==NR && /configs.*projectname\.conf/{f=1;next}f==0;END{ if(!f) { print "your line"}} ' file file

Czy to „plik plik” czy tylko „plik”?
webdevguy

Dzieje się tak, file fileponieważ wykonuje dwa przejścia przez ten sam plik. NR==FNRjest prawdziwe w pierwszym przebiegu, ale nie w drugim. To powszechny idiom w Awk.
tripleee

1

Odpowiedzi za pomocą grep są błędne. Musisz dodać opcję -x, aby dopasować całą linię, w przeciwnym razie wiersze takie jak #text to addbędą nadal pasować, gdy chcesz dodać dokładnie text to add.

Więc poprawne rozwiązanie to coś takiego:

grep -qxF 'include "/configs/projectname.conf"' foo.bar || echo 'include "/configs/projectname.conf"' >> foo.bar

1

Korzystanie z seda: wstawi na końcu wiersza. Oczywiście możesz również przekazywać zmienne jak zwykle.

grep -qxF "port=9033" $light.conf
if [ $? -ne 0 ]; then
  sed -i "$ a port=9033" $light.conf
else
    echo "port=9033 already added"
fi

Korzystanie z oneliner sed

grep -qxF "port=9033" $lightconf || sed -i "$ a port=9033" $lightconf

Używanie echa może nie działać w systemie root, ale będzie działać w ten sposób. Ale nie pozwoli ci zautomatyzować rzeczy, jeśli chcesz to zrobić, ponieważ może poprosić o hasło.

Miałem problem, gdy próbowałem edytować z poziomu roota dla konkretnego użytkownika. Samo dodanie $usernamepoprzedniego było dla mnie poprawką.

grep -qxF "port=9033" light.conf
if [ $? -ne 0 ]; then
  sudo -u $user_name echo "port=9033" >> light.conf
else
    echo "already there"    
fi

Witamy w stackoverflow! Przeczytaj uważnie pytanie przed wysłaniem odpowiedzi - OP zapytał, jak wstawić za pomocą seda
Markoorn

-1

Musiałem edytować plik z tak ograniczonymi uprawnieniami do zapisu sudo. działając na podstawie odpowiedzi ghostdog74 i używając pliku tymczasowego:

awk 'FNR==NR && /configs.*projectname\.conf/{f=1;next}f==0;END{ if(!f) { print "your line"}} ' file > /tmp/file
sudo mv /tmp/file file

To nie wygląda na poprawne, spowoduje utratę innych linii z pliku, gdy brakuje wiersza konfiguracji.
tripleee
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.