Podstawienie w pliku tekstowym ** bez ** wyrażeń regularnych

68

Muszę zastąpić część tekstu w pliku tekstowym zamiennikiem. Zwykle robiłbym coś takiego

sed -i 's/text/replacement/g' path/to/the/file

Problem polega na tym, że zarówno texti replacementsą złożone ciągi zawierające kreski, ukośniki, blackslashes, cytaty i tak dalej. Jeśli ucieknę, wszystkie niezbędne postacie w środku textstają się szybko nieczytelne. Z drugiej strony nie potrzebuję mocy wyrażeń regularnych: wystarczy dosłownie zastąpić tekst.

Czy istnieje sposób na zastąpienie tekstu bez użycia wyrażeń regularnych z jakimś poleceniem bash?

Pisanie skryptu, który to robi, byłoby raczej trywialne, ale sądzę, że coś już powinno istnieć.

Andrea
źródło
Niezbędne, aby to zrobić poprzez bash? Uproszczonym rozwiązaniem byłoby otwarcie w programie Word i wykonaniefind and replace all
Akash
17
@akash Ponieważ systemy, które bashzawsze są dostarczane z Microsoft Word? ;) Nie, tylko żartuję. OP może chcieć to zrobić na zdalnym komputerze lub w przypadku partii plików.
slhck
@slhck :) Cóż, myślę, że gedit powinien mieć podobną opcję
Akash
Opcją byłoby jakoś poprawnie uciec od wszystkiego przed przekazaniem sed, co jest prawdopodobnie daremnym wysiłkiem, biorąc pod uwagę wszystkie przełączniki i różnice platform.
l0b0
powiązane: stackoverflow.com/questions/29613304/…
Ciro Santilli 事件 改造 中心 法轮功 六四 事件

Odpowiedzi:

6

Kiedy nie potrzebujesz mocy wyrażeń regularnych, nie używaj jej. W porządku.
Ale tak naprawdę nie jest to wyrażenie regularne .

sed 's|literal_pattern|replacement_string|g'

Jeśli więc /masz problem, skorzystaj z niego |i nie musisz uciekać przed pierwszym.

ps: o komentarzach, zobacz także odpowiedź Stackoverflow na temat Escape a string for sed search pattern .


Aktualizacja: Jeśli nie masz nic przeciwko używaniu Perla, wypróbuj go \Qi \Etak,
perl -pe 's|\Qliteral_pattern\E|replacement_string|g'
RedGrittyBricksugeruje podobną sztuczkę z silniejszą składnią Perla w komentarzu tutaj

nik
źródło
Dziękuję, nie wiedziałem o różnicy między / a |
Andrea
64
Nie jestem pewien, czy ta odpowiedź jest przydatna ... Jedyna różnica między s|||i s///polega na tym, że postać oddzielająca jest inna i dlatego jedna postać nie musi uciekać. Równie dobrze możesz s###. Prawdziwy problem polega na tym, że OP nie chce się martwić o ucieczkę od treści literal_pattern(co nie jest wcale dosłowne i zostanie zinterpretowane jako wyrażenie regularne).
Benj
15
Nie uniknie to interpretacji innych znaków specjalnych. Co jeśli poszukasz w 1234.*aaaswoim rozwiązaniu, pasuje ono znacznie bardziej niż zamierzone 1234\.\*aaa.
Matteo
20
Ta odpowiedź nie powinna zostać zaakceptowana
Steven Lu
2
To całkowicie mija się z celem. Dopasowywany tekst może zawierać wszelkie dziwactwa. W moim przypadku jest to losowe hasło. Wiesz, jak ci idzie
Christian Bongiorno,
13
export FIND='find this'
export REPLACE='replace with this'
ruby -p -i -e "gsub(ENV['FIND'], ENV['REPLACE'])" path/to/file

Jest to jedyne w 100% bezpieczne rozwiązanie, ponieważ:

  • To podstacja statyczna, a nie wyrażenie regularne, nie trzeba niczego uciekać (w związku z tym lepsze niż używanie sed)
  • Nie zepsuje się, jeśli Twój ciąg zawiera }char (czyli lepszy niż przesłane rozwiązanie Perla)
  • Nie zerwie się z żadną postacią, ponieważ ENV['FIND']jest używana, a nie $FIND. Z $FINDlub w tekście wstawionym w kodzie Ruby, możesz trafić w błąd składniowy, jeśli ciąg zawiera nieskalowany '.
Nowaker
źródło
Musiałem użyć export FIND='find this; export REPLACE='replace with this';w moim skrypcie bash, tak że ENV['FIND']i ENV['replace']miał oczekiwaną wartość. Zastępowałem niektóre naprawdę długie zaszyfrowane ciągi w pliku. To był tylko bilet.
DMfll
To dobra odpowiedź, ponieważ jest wiarygodna, a rubin jest wszechobecny. Na podstawie tej odpowiedzi używam teraz tego skryptu powłoki .
loevborg
Niestety nie działa, gdy ZNAJDŹ zawiera wiele wierszy.
adrelanos
Nic nie stoi na przeszkodzie, aby działał z wieloma liniami w ZNAJDŹ. Użyj podwójnego cudzysłowu \ n.
Nowaker
7

replaceKomenda będzie to zrobić.

https://linux.die.net/man/1/replace

Zmień miejsce:

replace text replacement -- path/to/the/file

Do standardowego:

replace text replacement < path/to/the/file

Przykład:

$ replace '.*' '[^a-z ]{1,3}' <<EOF
> r1: /.*/g
> r2: /.*/gi
> EOF
r1: /[^a-z ]{1,3}/g
r2: /[^a-z ]{1,3}/gi

replacePolecenie pochodzi z MySQL lub MariaDB.

Derek Veit
źródło
3
weź pod uwagę, że zastąpienie jest przestarzałe i może nie być wykluczone w przyszłości
Rogelio,
1
Dlaczego, u licha, takie podstawowe polecenia mają bazę danych?
masterxilo,
3
@masterxilo Lepszym pytaniem może być - dlaczego takie podstawowe polecenie nie pochodzi z nowoczesnymi systemami operacyjnymi? ;-)
Mark Thomson
3

Możesz także użyć \Qmechanizmu perla do „ cytowania (wyłączania) metaznaków wzorca

perl -pe 'BEGIN {$text = q{your */text/?goes"here"}} s/\Q$text\E/replacement/g'
Glenn Jackman
źródło
3
Lubperl -pe 's(\Qyour */text/?goes"here")(replacement)' file
RedGrittyBrick
3

sprawdź mój skrypt Perla. robi dokładnie to, czego potrzebujesz, bez niejawnego lub jawnego użycia wyrażeń regularnych:

https://github.com/Samer-Al-iraqi/Linux-str_replace

str_replace Search Replace File # replace in File in place

STDIN | str_replace Search Replace # to STDOUT

bardzo przydatny, prawda? Musiałem się tego nauczyć. ponieważ naprawdę tego potrzebuję.

Samer Ata
źródło
2

Możesz to zrobić, unikając swoich wzorów. Lubię to:

keyword_raw='1/2/3'
keyword_regexp="$(printf '%s' "$keyword_raw" | sed -e 's/[]\/$*.^|[]/\\&/g')"
# keyword_regexp is now '1\/2\/3'

replacement_raw='2/3/4'
replacement_regexp="$(printf '%s' "$replacement_raw" | sed -e 's/[\/&]/\\&/g')"
# replacement_regexp is now '2\/3\/4'

echo 'a/b/c/1/2/3/d/e/f' | sed -e "s/$keyword_regexp/$replacement_regexp/"
# the last command will print 'a/b/c/2/3/4/d/e/f'

Kredyty dla tych rozwiązań można znaleźć tutaj: https://stackoverflow.com/questions/407523/escape-a-string-for-a-sed-replace-pattern

Uwaga 1: działa to tylko w przypadku niepustych słów kluczowych. Puste słowa kluczowe nie są akceptowane przez sed ( sed -e 's//replacement/').

Uwaga 2: niestety nie znam popularnego narzędzia, które NIE użyłoby wyrażeń regularnych do rozwiązania problemu. Możesz napisać takie narzędzie w Rust lub C, ale domyślnie go nie ma.

VasyaNovikov
źródło
To całkowicie pomija punkt PO. Oczywiście możesz uciec od tego wzoru, ale w przypadku niektórych wzorów jest to uciążliwe.
icecreamsword
@icecreamsword czy przeczytałeś moją odpowiedź poniżej pierwszego wiersza? Skrypt ucieka automatycznie .
VasyaNovikov
1

Zebrałem kilka innych odpowiedzi i wymyśliłem to:

function unregex {
   # This is a function because dealing with quotes is a pain.
   # http://stackoverflow.com/a/2705678/120999
   sed -e 's/[]\/()$*.^|[]/\\&/g' <<< "$1"
}
function fsed {
   local find=$(unregex "$1")
   local replace=$(unregex "$2")
   shift 2
   # sed -i is only supported in GNU sed.
   #sed -i "s/$find/$replace/g" "$@"
   perl -p -i -e "s/$find/$replace/g" "$@"
}
Xiong Chiamiov
źródło
Nie działa z nowymi liniami. Nie pomaga też uciec przed nowymi liniami \n. Jakieś rozwiązanie?
adrelanos
1

Możesz użyć ph_'s str_replace :

php -R 'echo str_replace("\|!£$%&/()=?^\"'\''","replace",$argn),PHP_EOL;'<input.txt >output.txt

Uwaga: Jednak nadal będziesz musiał unikać pojedynczych 'i podwójnych cudzysłowów ".

simlev
źródło
0

Odpowiednik Node.JS @Nowaker:

export FNAME='moo.txt'
export FIND='search'
export REPLACE='rpl'
node -e 'fs=require("fs");fs.readFile(process.env.FNAME,"utf8",(err,data)=>{if(err!=null)throw err;fs.writeFile(process.env.FNAME,data.replace(process.env.FIND,process.env.REPLACE),"utf8",e=>{if(e!=null)throw e;});});'
W
źródło
0

Oto jeszcze jeden „prawie” działający sposób.

Użyj vi lub vim.

Utwórz plik tekstowy z podstawieniem:

:% sno / my search string \\ "-: # 2; g ('. j'); \\"> / my replaceestring = \\ "bac) (o: # 46; \\"> /
: x

następnie uruchom vi lub vim z wiersza poleceń:

vi -S commandfile.txt path/to/the/file

:% sno to polecenie vi do wyszukiwania i zastępowania bez magii.

/ jest moim wybranym separatorem.

: x zapisuje i wychodzi vi.

Musisz uciec z ukośników odwrotnych „\” Forwardlash ”/„ można zastąpić np. Znakiem zapytania ”? lub coś innego, czego nie ma w wyszukiwaniu lub zamień ciąg, potok „|” nie działało dla mnie tys.

ref: https://stackoverflow.com/questions/6254820/perform-a-non-regex-search-replace-in-vim https://vim.fandom.com/wiki/Search_without_need_to_escape_slash http://linuxcommand.org/ lc3_man_pages / vim1.html

Samuel Åslund
źródło