Sostituzione nel file di testo ** senza ** espressioni regolari


68

Devo sostituire del testo all'interno di un file di testo con un sostituto. Di solito farei qualcosa del genere

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

Il problema è che entrambi texte replacementsono stringhe complesse contenenti trattini, barre, blackslashes, citazioni e così via. Se sfuggo a tutti i personaggi necessari all'interno textdella cosa diventa rapidamente illeggibile. D'altra parte non ho bisogno del potere delle espressioni regolari: ho solo bisogno di sostituire il testo letteralmente.

C'è un modo per fare la sostituzione del testo senza usare espressioni regolari con qualche comando bash?

Sarebbe piuttosto banale scrivere una sceneggiatura che faccia questo, ma immagino che dovrebbe già esistere qualcosa.


È necessario farlo attraverso bash? Una soluzione semplicistica sarebbe quella di aprire in Word e fare unfind and replace all
Akash

17
@akash Perché i sistemi che vengono bashsempre forniti con Microsoft Word? ;) No, sto solo scherzando. L'OP potrebbe volerlo fare su un computer remoto o per un batch di file.
Slhck,

@slhck :) Beh, immagino che gedit dovrebbe avere un'opzione simile
Akash

Un'opzione sarebbe in qualche modo sfuggire correttamente a tutto prima di passarlo sed, il che è probabilmente uno sforzo inutile considerando tutti gli switch e le differenze della piattaforma.
l0b0

Risposte:


6

Quando non hai bisogno del potere delle espressioni regolari, non usarlo. Questo va bene.
Ma questa non è davvero un'espressione regolare .

sed 's|literal_pattern|replacement_string|g'

Quindi, se /è il tuo problema, usa |e non hai bisogno di sfuggire al primo.

ps: riguardo ai commenti, vedi anche questa risposta StackOverflow su Esci da una stringa per il modello di ricerca sed .


Aggiornamento: Se stai bene usando Perl provalo con \Qe in \Equesto modo,
perl -pe 's|\Qliteral_pattern\E|replacement_string|g'
RedGrittyBrickha anche suggerito un trucco simile con una sintassi Perl più forte in un commento qui


Grazie, non sapevo della differenza tra / e |
Andrea,

64
Non sono sicuro che questa risposta sia utile ... L'unica differenza tra s|||e s///è che il carattere separatore è diverso e quindi un personaggio non ha bisogno di scappare. Puoi fare altrettanto s###. Il vero problema qui è che l'OP non vuole preoccuparsi di sfuggire al contenuto di literal_pattern(che non è affatto letterale e verrà interpretato come una regex).
Benj,

15
Ciò non eviterà l'interpretazione di altri caratteri speciali. Che cosa succede se la ricerca 1234.*aaacon la tua soluzione corrisponde molto più del previsto 1234\.\*aaa.
Matteo

20
Questa risposta non dovrebbe essere accettata
Steven Lu

2
Questo manca completamente il punto. Il testo da abbinare potrebbe contenere qualsiasi stranezza. Nel mio caso è una password casuale. Sai come vanno
Christian Bongiorno,

13
export FIND='find this'
export REPLACE='replace with this'
ruby -p -i -e "gsub(ENV['FIND'], ENV['REPLACE'])" path/to/file

Questa è l'unica soluzione sicura al 100% qui, perché:

  • È una sottostazione statica, non una regexp, non c'è bisogno di sfuggire a nulla (quindi, superiore all'uso sed)
  • Non si romperà se la tua stringa contiene }char (quindi, superiore a una soluzione Perl inviata)
  • Non si romperà con nessun personaggio, perché non ENV['FIND']viene usato $FIND. Con $FINDo il tuo testo integrato nel codice Ruby, potresti riscontrare un errore di sintassi se la stringa contenesse un carattere senza caratteri di escape '.

Ho dovuto usare export FIND='find this; export REPLACE='replace with this';nel mio script bash in modo che ENV['FIND']e ENV['replace']aveva i valori attesi. Stavo sostituendo alcune stringhe crittografate davvero lunghe in un file. Questo era solo il biglietto.
DMfll

Questa è una buona risposta perché è affidabile e il rubino è onnipresente. Sulla base di questa risposta ora uso questo script di shell .
Loevborg,

Sfortunatamente non funziona quando FIND contiene più righe.
adrelanos,

Non c'è nulla che gli impedisca di lavorare con più linee in FIND. Usa virgolette doppie \ n.
Nowaker il

7

Il replacecomando lo farà.

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

Cambia sul posto:

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

Per stdout:

replace text replacement < path/to/the/file

Esempio:

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

Il replacecomando viene fornito con MySQL o MariaDB.


3
tieni conto che la sostituzione è obsoleta e potrebbe non essere disponibile in futuro
Rogelio

1
Perché mai un tale comando di base viene fornito con un database?
masterxilo,

3
@masterxilo Una domanda migliore potrebbe essere: perché un tale comando di base non arriva con i moderni sistemi operativi? ;-)
Mark Thomson il


3

controlla il mio script Perl. fa esattamente ciò di cui hai bisogno senza l'uso implicito o esplicito dell'espressione regolare:

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

molto utile vero? Ho dovuto imparare il Perl per farlo. perché ne ho davvero bisogno.


2

Puoi farlo sfuggendo ai tuoi schemi. Come questo:

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'

I crediti per questa soluzione vanno qui: https://stackoverflow.com/questions/407523/escape-a-string-for-a-sed-replace-pattern

Nota 1: funziona solo con parole chiave non vuote. Le parole chiave vuote non sono accettate da sed ( sed -e 's//replacement/').

Nota2: purtroppo non conosco uno strumento popolare che NON utilizzi regexp-s per risolvere il problema. Puoi scrivere uno strumento simile in Rust o C, ma non è presente per impostazione predefinita.


Questo manca completamente il punto del PO. Ovviamente puoi sfuggire allo schema, ma per alcuni schemi questo è noioso.
icecreamsword,

@icecreamsword hai letto la mia risposta sotto la prima riga? Lo script scappa automaticamente .
VasyaNovikov,

1

Ho messo insieme alcune altre risposte e ho trovato questo:

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" "$@"
}

Non funziona con le nuove righe. Inoltre non aiuta a sfuggire alle nuove linee con \n. Qualche soluzione?
adrelanos,

1

Puoi usare str_replace di php :

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

Nota: dovresti comunque sfuggire alle virgolette singole 'e alle doppie virgolette ".


0

Equivalente Node.JS di @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;});});'

0

Ecco un altro modo "quasi" di lavoro.

Usa vi o vim.

Crea un file di testo con la tua sostituzione in esso:

:% sno / la mia stringa di ricerca \\ "-: # 2; g ('. j'); \\"> / my replacestring = \\ "bac) (o: # 46; \\"> /
:X

quindi eseguire vi o vim dalla riga di comando:

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

:% sno è il comando vi per cercare e sostituire senza magia.

/ è il mio separatore scelto.

: x salva ed esce vi.

È necessario sfuggire alle barre rovesciate '\' the forwardslash '/' potrebbe essere sostituito, ad esempio, con un punto interrogativo '?' o qualcos'altro che non è nella tua ricerca o stringa di sostituzione, pipe '|' non ha funzionato per me comunque.

rif: 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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.