Zamień całą linię zawierającą ciąg za pomocą Sed

319

Mam plik tekstowy, który ma określoną linię coś w rodzaju

sometext sometext sometext TEXT_TO_BE_REPLACED sometext sometext sometext

Muszę zastąpić całą powyższą linię

This line is removed by the admin.

Szukanym słowem kluczowym jest TEXT_TO_BE_REPLACED

Muszę do tego napisać skrypt powłoki. Jak mogę to osiągnąć za pomocą sed?

Rahul
źródło
@Scharron Próbowałem sed -rne 's / (TEXT_TO_BE_REPLACED) \ s + \ w + / \ 1 rrr / xxx' Ale to po prostu zastąpiłoby rrr przez xxx w ciągu, jeśli jest obecny rrr. W moim przypadku tekst nie jest naprawiony ... wszystko co mam to TEXT_TO_BE_REPLACED.
Rahul
to nie jest prawidłowe wyrażenie sed. Czy na pewno chcesz tego z sed? Dowolny język programowania z dobrymi wyrażeniami regularnymi może zastąpić użytkownika (wyszukać funkcję podrzędną). To byłoby łatwiejsze niż samodzielne zrozumienie
Scharron
@Scharron Cóż, należy to zrobić za pomocą skryptu powłoki, więc sądzę, że sed jest moim najlepszym wyborem.
Rahul

Odpowiedzi:

483

Możesz użyć polecenia zmiany, aby zastąpić całą linię, oraz -iflagi, aby wprowadzić zmiany w miejscu. Na przykład, używając GNU sed:

sed -i '/TEXT_TO_BE_REPLACED/c\This line is removed by the admin.' /tmp/foo
Todd A. Jacobs
źródło
4
Zauważ, że potrzebujesz spacji przed c \. Właśnie edytowałem, aby to dodać.
Marcus Downing
27
@MarcusDowning GNU sed nie wymaga miejsca; to działa dobrze, jak pierwotnie opublikowano. Jeśli twój konkretny sed wymaga miejsca, to z całą pewnością zanotuj, który sed jest niezgodny i dodaj niezbędne wywołanie jako komentarz. Nie zmieniaj jednak działającego kodu w zaakceptowanej odpowiedzi.
Todd A. Jacobs
7
Jak mogę użyć zmiennej zamiast tekstu „To ...”? Jeśli zastąpię go zmienną $ zmienną, nie drukuje ona zawartości, ale nazwę zmiennej.
Steven
18
Występuje problem, c\ gdy bezpośrednio po nim następuje zmienna: …c\$VAR…ukośnik odwróci się od dolara. W tym przypadku (bash / sed na Ubuntu 15.10) miał napisać…c\\$VAR…
sty
6
na Macintosh: sed -i '' '/TEXT_TO_BE_REPLACED/c\This line is removed by the admin.' /tmp/foo; (gdy pierwszy parametr jest pusty, edytuje się w pliku, w przeciwnym razie tworzy kopię zapasową)
AndreDurao 10.04.2018
152

.*Aby zastąpić całą linię, musisz użyć symboli wieloznacznych ( ) przed i po:

sed 's/.*TEXT_TO_BE_REPLACED.*/This line is removed by the admin./'
Thor
źródło
Dziękuję, mój działał: sed 's /.* <wyrażenie>. * / <Wyrażenie> SE_LABEL = ABC <wyrażenie> / g' MYR2.xml> test.txt
Nasri Najib
9
Działa to w systemie Mac OS X Yosemite, z wyjątkiem tego, że używam flag -i i -e w następujący sposób:sed -i -e "s/.*search_string.*/Replacement_line/' file_being_searched.txt
Kent Bull
1
@KentJohnson Myślę, że masz niedopasowane cytaty w swoim poleceniu.
HashHazard
@ MBarnett masz rację, powinienem tam mieć dwa podwójne cytaty.
Kent Bull
2
Tylko dla pełnych informacji. Aby zrobić to na miejscu, można dodać -iopcję
Temak
20

Przyjęta odpowiedź nie działała dla mnie z kilku powodów:

  • moja wersja sed nie lubi -irozszerzenia o zerowej długości
  • składnia c\polecenia jest dziwna i nie mogłem go uruchomić
  • Nie zdawałem sobie sprawy, że niektóre z moich problemów pochodzą od nieskalowanych cięć

Oto wymyślone przeze mnie rozwiązanie, które moim zdaniem powinno działać w większości przypadków:

function escape_slashes {
    sed 's/\//\\\//g' 
}

function change_line {
    local OLD_LINE_PATTERN=$1; shift
    local NEW_LINE=$1; shift
    local FILE=$1

    local NEW=$(echo "${NEW_LINE}" | escape_slashes)
    sed -i .bak '/'"${OLD_LINE_PATTERN}"'/s/.*/'"${NEW}"'/' "${FILE}"
    mv "${FILE}.bak" /tmp/
}

Przykładowe użycie do rozwiązania postawionego problemu:

change_line "TEXT_TO_BE_REPLACED" "This line is removed by the admin." yourFile
skensell
źródło
18

Odpowiedź powyżej:

sed -i '/TEXT_TO_BE_REPLACED/c\This line is removed by the admin.' /tmp/foo

Działa dobrze, jeśli zastępujący ciąg / linia nie jest zmienną.

Problem polega na tym, że w Redhat 5 \po cucieczce $. Podwójny \\też nie działał (przynajmniej na Redhat 5).

Dzięki testom trafień i próbom odkryłem, że ciąg \po cjest zbędny, jeśli zastępujący ciąg / linia jest tylko jedną linią. Więc nie użyłem \po c, użyłem zmiennej jako pojedynczej linii zastępczej i to była radość.

Kod wyglądałby mniej więcej tak:

sed -i "/TEXT_TO_BE_REPLACED/c $REPLACEMENT_TEXT_STRING" /tmp/foo

Zwróć uwagę na użycie podwójnych cudzysłowów zamiast pojedynczych cudzysłowów.

użytkownik4256255
źródło
wciąż możesz używać pojedynczych cudzysłowów: sed -i '/ TEXT_TO_BE_REPLACED / c' "$ VARIABLE" '' / tmp / foo
Alistair McIntyre
4

Wszystkie dotychczasowe odpowiedzi zakładają, że wiesz coś o tekście, który ma zostać zastąpiony, co ma sens, ponieważ o to poprosił OP. Podaję odpowiedź, która zakłada, że ​​nic nie wiesz o tekście, który ma zostać zastąpiony, i że w pliku może znajdować się osobna linia z taką samą lub podobną treścią, której nie chcesz zastępować. Ponadto zakładam, że znasz numer linii, która ma zostać zastąpiona.

Poniższe przykłady pokazują usuwanie lub zmianę tekstu według określonych numerów linii:

# replace line 17 with some replacement text and make changes in file (-i switch)
# the "-i" switch indicates that we want to change the file. Leave it out if you'd
#   just like to see the potential changes output to the terminal window.
# "17s" indicates that we're searching line 17
# ".*" indicates that we want to change the text of the entire line
# "REPLACEMENT-TEXT" is the new text to put on that line
# "PATH-TO-FILE" tells us what file to operate on
sed -i '17s/.*/REPLACEMENT-TEXT/' PATH-TO-FILE

# replace specific text on line 3
sed -i '3s/TEXT-TO-REPLACE/REPLACEMENT-TEXT/'
b_laoshi
źródło
3

do manipulowania plikami konfiguracyjnymi

wpadłem na to rozwiązanie zainspirowane odpowiedzią Skensell

configLine [searchPattern] [replaceLine] [filePath]

to będzie:

  • utwórz plik, jeśli nie istnieje
  • zamień całą linię (wszystkie linie), do której pasuje searchPattern
  • dodaj replaceLine na końcu pliku, jeśli wzorzec nie został znaleziony

Funkcjonować:

function configLine {
  local OLD_LINE_PATTERN=$1; shift
  local NEW_LINE=$1; shift
  local FILE=$1
  local NEW=$(echo "${NEW_LINE}" | sed 's/\//\\\//g')
  touch "${FILE}"
  sed -i '/'"${OLD_LINE_PATTERN}"'/{s/.*/'"${NEW}"'/;h};${x;/./{x;q100};x}' "${FILE}"
  if [[ $? -ne 100 ]] && [[ ${NEW_LINE} != '' ]]
  then
    echo "${NEW_LINE}" >> "${FILE}"
  fi
}

szalona magia statusu wyjścia pochodzi z https://stackoverflow.com/a/12145797/1262663

Ziarno
źródło
2
bash-4.1$ new_db_host="DB_HOSTNAME=good replaced with 122.334.567.90"
bash-4.1$ 
bash-4.1$ sed -i "/DB_HOST/c $new_db_host" test4sed
vim test4sed
'
'
'
DB_HOSTNAME=good replaced with 122.334.567.90
'

to działa dobrze

nkl
źródło
1

W moim makefile używam tego:

@sed -i '/.*Revision:.*/c\'"`svn info -R main.cpp | awk '/^Rev/'`"'' README.md

PS: NIE zapominaj, że -i zmienia tak naprawdę tekst w pliku ... więc jeśli wzorzec zdefiniowany jako „Rewizja” ulegnie zmianie, zmienisz również wzorzec, który chcesz zastąpić.

Przykładowe dane wyjściowe:

Projekt Abc napisany przez Johna Doe

Wersja: 1190

Więc jeśli ustawisz wzorzec „Wersja: 1190”, to oczywiście nie będzie to samo, co zdefiniowałeś jako „Wersja:” tylko ...

Martin Pfeffer
źródło
1
cat find_replace | while read pattern replacement ; do
sed -i "/${pattern}/c ${replacement}" file    
done 

plik find_replace zawiera 2 kolumny, c1 z dopasowanym wzorcem, c2 z zamiennikiem, pętla sed zastępuje każdą linię zawierającą jeden ze wzorca zmiennej 1

sjordi
źródło
Nie, jest to błędne z kilku powodów. Uruchom sedraz z plikiem skryptu zawierającym wszystkie zamienniki, które chcesz wykonać. Uruchomiony sed -ina tym samym pliku wielokrotnie to straszne antywzorzec projektowy.
tripleee,
0

Jest podobny do powyższego ..

sed 's/[A-Za-z0-9]*TEXT_TO_BE_REPLACED.[A-Za-z0-9]*/This line is removed by the admin./'
Annapureddy Hari
źródło
3
Że zmienia FOO=TEXT_TO_BE_REPLACEDsię FOO=This line ...tak nie spełnia specyfikacji.
Jens
Tak .. Naszym wymogiem jest zastąpienie całej linii komunikatem „Ta linia została usunięta przez administratora”. jeśli znaleźliśmy kluczowe wzorce „TEXT_TO_BE_REPLACED”. Powyższe polecenie jest satysfakcjonujące. Popraw mnie, jeśli źle rozumiem. @Jens
Annapureddy Hari
@AnnapureddyHari ta odpowiedź nie działa, jeśli tekst przed lub po ciągu wyszukiwania zawiera w sobie coś oprócz A-Za-z0-9. Nie udaje się, jeśli istnieje znak równości, jak zauważył Jens. Część „FOO =” pozostanie; nie zastąpiłeś całej linii. Ten kod jest krótkowzroczny o tym, co może być w pliku. Jeśli masz na myśli symbol wieloznaczny, powinieneś umieścić symbol wieloznaczny, jak pokazuje odpowiedź Thora.
msouth
0

Poniższe polecenie działa dla mnie. Który działa ze zmiennymi

sed -i "/\<$E\>/c $D" "$B"
Ritesh Borkar
źródło
Ale moim nowym wymaganiem jest pomijanie komentarza (zaczyna się od #) wiersza podczas zastępowania. Gdy zastępujemy całą linię, to również zastąpi linie komentowane, a skończysz na zduplikowanych właściwościach. Jeśli ktoś ma na to rozwiązanie, proszę dać mi znać.
Ritesh Borkar,
-1

I bardzo często korzystają z regex danych ekstrakt z plików używanych że po prostu zastąpić dosłownego cytatu \"z //niczego :-)

cat file.csv | egrep '^\"([0-9]{1,3}\.[0-9]{1,3}\.)' | sed  s/\"//g  | cut -d, -f1 > list.txt
STAM
źródło