Quali personaggi devo scappare quando uso sed in uno script sh?


248

Prendi il seguente script:

#!/bin/sh
sed 's/(127\.0\.1\.1)\s/\1/' [some file]

Se provo a eseguirlo sh( dashqui), fallirà a causa delle parentesi, che devono essere salvate. Ma non ho bisogno di sfuggire alle barre rovesciate stesse (tra gli ottetti, o nel \so \1). Qual è la regola qui? Che dire di quando devo usare {...}o [...]? C'è un elenco di ciò che faccio e che non devo scappare?


1
Ecco una funzione bash per convertire i percorsi da utilizzare con SED:function sedPath { path=$((echo $1|sed -r 's/([\$\.\*\/\[\\^])/\\\1/g'|sed 's/[]]/\[]]/g')>&1) } #Escape path for use with sed
user2428118


Dura lex, sed sed
Nemo,

Risposte:


282

Ci sono due livelli di interpretazione qui: il guscio e sed.

Nella shell, tutto tra virgolette singole viene interpretato letteralmente, ad eccezione delle virgolette singole stesse. Puoi effettivamente avere una virgoletta singola tra virgolette singole scrivendo '\''(chiudi virgoletta singola, virgoletta singola letterale, apri virgoletta singola).

Sed usa espressioni regolari di base . In un BRE, per poterli trattare letteralmente, i personaggi $.*[\^devono essere citati precedendoli da una barra rovesciata, ad eccezione dei set di caratteri interni ( […]). Lettere, cifre e (){}+?|non devono essere citati (è possibile cavarsela citando alcuni di questi in alcune implementazioni). Le sequenze \(, \), \n, e in alcune implementazioni \{, \}, \+, \?, \|e altri backslash + alfanumerici hanno un significato particolare. Puoi evitare di non citare $^alcune posizioni in alcune implementazioni.

Inoltre, è necessario prima una barra rovesciata /se deve apparire nella regex al di fuori delle espressioni parentesi. Puoi scegliere un carattere alternativo come delimitatore scrivendo, ad esempio, s~/dir~/replacement~oppure \~/dir~p; avrai bisogno di una barra rovesciata prima del delimitatore se vuoi includerlo nel BRE. Se scegli un personaggio che ha un significato speciale in un BRE e vuoi includerlo letteralmente, avrai bisogno di tre barre rovesciate; Non lo consiglio, poiché potrebbe comportarsi diversamente in alcune implementazioni.

In breve, per sed 's/…/…/':

  • Scrivi la regex tra virgolette singole.
  • Utilizzare '\''per finire con una singola citazione nella regex.
  • Metti una barra rovesciata prima $.*/[\]^e solo quei caratteri (ma non all'interno delle espressioni tra parentesi). (Tecnicamente non dovresti mettere una barra rovesciata prima, ]ma non conosco un'implementazione che tratta ]e in modo \]diverso al di fuori delle espressioni parentesi.)
  • All'interno di un'espressione tra parentesi, per -essere trattato alla lettera, assicurati che sia il primo o l'ultimo ( [abc-]o [-abc], non [a-bc]).
  • All'interno di un'espressione tra parentesi quadre, per ^essere trattati letteralmente, assicurarsi che sia non prima (uso [abc^], non è [^abc]).
  • Per includere ]nell'elenco dei caratteri corrispondenti a un'espressione di parentesi quadre, impostalo come primo carattere (o il primo dopo ^per un set negato): []abc]oppure [^]abc](not [abc]]nor[abc\]] ).

Nel testo di sostituzione:

  • &e \devono essere citati precedendoli da una barra rovesciata, così come il delimitatore (di solito /) e le nuove righe.
  • \seguito da una cifra ha un significato speciale. \seguita da una lettera ha un significato speciale (caratteri speciali) in alcune implementazioni e \seguita da altri mezzi \co in cbase all'implementazione.
  • Con virgolette singole attorno all'argomento ( sed 's/…/…/'), utilizzare '\''per inserire una virgoletta singola nel testo sostitutivo.

Se il regex o il testo sostitutivo provengono da una variabile shell, ricordatelo

  • La regex è una BRE, non una stringa letterale.
  • Nella regex, una nuova riga deve essere espressa come \n(che non corrisponderà mai a meno che non si disponga di altro sedcodice per aggiungere caratteri di nuova riga allo spazio del modello). Ma nota che non funzionerà all'interno delle espressioni parentesi con alcune sedimplementazioni.
  • Nel testo sostitutivo, &, \e ritorni a capo devono essere citato.
  • Il delimitatore deve essere citato (ma non all'interno delle espressioni tra parentesi).
  • Utilizzare le virgolette doppie per l'interpolazione: sed -e "s/$BRE/$REPL/".

Sfuggendo al carattere jolly effettivo (*) è possibile utilizzare una doppia barra rovesciata ( \\*). Esempio:echo "***NEW***" | sed /\\*\\*\\*NEW\\*\\*\\*/s/^/#/
danger89

43

Il problema che stai riscontrando non è dovuto all'interpolazione della shell e agli escape: è perché stai tentando di utilizzare la sintassi estesa delle espressioni regolari senza passare dall'opzione -ro --regexp-extended.

Cambia la tua linea sed da

sed 's/(127\.0\.1\.1)\s/\1/' [some file]

per

sed -r 's/(127\.0\.1\.1)\s/\1/' [some file]

e funzionerà come credo tu intenda.

Di default sed usa usa espressioni regolari di base (pensa allo stile grep), che richiederebbe la seguente sintassi:

sed 's/\(127\.0\.1\.1\)[ \t]/\1/' [some file]

Ho avuto di nuovo questo problema e ho dimenticato di scorrere verso il basso per trovare la soluzione che ho votato l'ultima volta. Grazie ancora.
isaaclw,

Molte grazie. L'aggiunta -rcome opzione era ciò che era necessario nel mio caso.
Ciao Arrivederci

15

A meno che non si desideri interpolare una variabile di shell nell'espressione sed, utilizzare le virgolette singole per l'intera espressione perché causano l'interpretazione così com'è, tra cui barre rovesciate.

Quindi, se si desidera che sed veda s/\(127\.0\.1\.1\)\s/\1/intorno a virgolette singole e la shell non toccherà le parentesi o le barre rovesciate. Se devi interpolare una variabile di shell, metti solo quella parte tra virgolette. Per esempio

sed 's/\(127\.0\.1\.1\)/'"$ip"'/'

Questo ti risparmierà la fatica di ricordare quali metacaratteri shell non sfuggono alle doppie virgolette.


Voglio sedvedere s/(127\.0\.1\.1)/..., ma inserirlo in uno script di shell così com'è non funziona. Quello che stai dicendo sul guscio che non tocca le parentesi sembra sbagliato. Ho elaborato la mia domanda per elaborarla.
detenere il

3
La shell non tocca le parentesi. Hai bisogno dei backslases perché sed ha bisogno di vederli. sed 's/(127\.0\.1\.1)/IP \1/'fallisce perché sed ha bisogno di vedere \(e \)per la sintassi di gruppo, non (e ).
Kyle Jones,

facepalm Non è nella pagina man, ma è in alcuni manuali online che ho trovato. È normale per regex, perché non ho mai dovuto usarlo nelle librerie regex (in, ad es. Python)?
detenere il

3
Per i comandi Unix tradizionali, esistono espressioni regolari di base ed espressioni regolari estese. Dettagli . sed usa espressioni regolari di base, quindi le barre rovesciate sono necessarie per la sintassi del gruppo. Perl e Python andarono oltre le espressioni regolari anche estese. Mentre cercavo in giro, ho trovato un diagramma estremamente informativo che illustra quale confusione confusa evociamo quando diciamo con disinvoltura "espressione regolare".
Kyle Jones,

1
Vorrei anche aggiungere che l'unico carattere che non può essere utilizzato tra virgolette singole è una virgoletta singola.
enzotib,
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.