Come passare una variabile contenente barre a sed


100

Come si passa una variabile contenente barre come modello a sed?

Ad esempio, se ho la seguente variabile:

var="/Users/Documents/name/file"

Voglio passarlo in questo sedmodo:

sed "s/$var/replace/g" "$file"

Tuttavia ottengo errori. Come posso aggirare il problema?

Risposte:


193

Utilizza un delimitatore regex alternativo che sedti consente di utilizzare qualsiasi delimitatore ( inclusi i caratteri di controllo ):

sed "s~$var~replace~g" $file

2
Non funziona Provo sed -i "s ~ blah ~ $ var ~ g file" e ottengo: "sed -e espressione # 1, char 70: comando` s 'non terminato ". Ho confermato che il colpevole è una barra in $ var. Le varianti di questa soluzione a questo problema sono ovunque e nessuna di esse funziona. Sed è stato aggiornato di recente? Sono su v4.2.2 su Ubuntu 14.04.4 LTS.
Paul Ericson

Dipende dal valore di$var
anubhava

1
Non funziona con ~ come ha osservato @PaulEricson, ma funziona ancora con |
Kenny Ho

1
L'ultimo "~" (dopo g) sembra non essere necessario, almeno per AWS Amazon Linux (basato su CentOS)
Saúl Martínez Vidals

1
Mi hai salvato la giornata
utente7131571

62

Una risposta bash pura: usa l' espansione dei parametri per eseguire l'escape della barra rovesciata da qualsiasi barra nella variabile:

var="/Users/Documents/name/file"
sed "s/${var//\//\\/}/replace/g" $file

1
Questo è un buon modo, perché non sempre sai quali caratteri contiene la variabile. Quindi puoi sempre sfuggire al delimitatore sed in caso di dubbio. Ad esempio: sed "s: $ {var //: / \\:}: replace: g" $ file
Stephane L

Bellissimo. Ho reso alcuni dei miei script di rinominazione molto più puliti.
Cloud

Ho imparato qualcosa di bello oggi, grazie. Dovrebbe essere la risposta accettata.
Steve Kehlet

6
Po 'di chiarezza sulla dell'essere modello utilizzato qui: ${parameter/pattern/string}. Quindi, in questo caso, il parametro è var, il modello è /\/e la stringa è \\/. Tutte le istanze del modello vengono sostituite perché il modello inizia con a /.
Josh

1
@josh, quindi la barra iniziale del pattern non fa parte del pattern da sostituire.
glenn jackman

32

Un altro modo per farlo, sebbene più brutto della risposta di anubhava, è sfuggire a tutti i backslash varusando un altro sedcomando:

var=$(echo "$var" | sed 's/\//\\\//g')

quindi, questo funzionerà:

sed "s/$var/replace/g" $file

12

Usare /in sed come delimitatore entrerà in conflitto con le barre nella variabile quando sostituite e di conseguenza otterrai un errore. Un modo per aggirare questo problema consiste nell'usare un altro delimitatore univoco rispetto a qualsiasi carattere presente in quella variabile.

var="/Users/Documents/name/file"

puoi usare il personaggio octothorpe che si adatta all'occasione (o qualsiasi altro personaggio che non sia /per facilità d'uso)

sed "s#$var#replace#g" 

o

sed 's#$'$var'#replace#g'

questo è adatto quando la variabile non contiene spazi

o

sed 's#$"'$var'"#replace#g'

È più saggio usare quanto sopra visto che siamo interessati a sostituire qualunque cosa sia in quella variabile solo rispetto alla doppia citazione dell'intero comando che può far sì che la tua shell interpreti qualsiasi carattere che potrebbe essere considerato un carattere di shell speciale nella shell.


Non funziona Provo sed -i "s ~ blah ~ $ var ~ g file" e ottengo: "sed -e espressione # 1, char 70: comando` s 'non terminato ". Ho confermato che il colpevole è una barra in $ var. Le varianti di questa soluzione a questo problema sono ovunque e nessuna di esse funziona. Sed è stato aggiornato di recente? Sono su v4.2.2 su Ubuntu 14.04.4 LTS.
Paul Ericson

@PaulEricson Non sono in grado di riprodurlo su qualsiasi Ubuntu disponibile. Se il tuo $varcontiene ~, dovrai ovviamente scegliere un delimitatore diverso. L'errore "non terminato" sembra che il tuo $varcontenga una nuova riga; potresti essere in grado di risolverlo facendo l'escape di backslash ogni nuova riga nel valore, ma non penso che sia completamente portabile. Questo è in linea di massima estraneo al problema delle barre nel valore.
tripleee

@PaulEricson Oh e la tua citazione non è corretta, non dovresti avere il nome del file tra virgolette. sed -i "s~blah~$var~g" filecon virgolette solo intorno allo sedscript attuale .
tripleee

5

Questa è una vecchia domanda, ma nessuna delle risposte qui discute le operazioni se non s/from/to/in modo molto dettagliato.

La forma generale di una seddichiarazione è

*address* *action*

dove indirizzo può essere un intervallo regex o un intervallo di numeri di riga (o vuoto, nel qual caso l' azione viene applicata a ogni riga di input). Quindi per esempio

sed '1,4d' file

cancellerà le righe da 1 a 4 (l' indirizzo è l'intervallo di numeri di riga 1,4e l' azione è il dcomando di eliminazione); e

sed '/ick/,$s/foo/bar/' file

sostituirà la prima occorrenza di foocon barsu qualsiasi riga tra la prima corrispondenza della regex icke la fine del file (l' indirizzo è l'intervallo /ick/,$e l' azione è il scomando sostitutivo s/foo/bar/).

In questo contesto, se ickprovenisse da una variabile, si potrebbe fare

sed "/$variable/,\$s/foo/bar/"

(si noti l'uso di virgolette doppie anziché singole, in modo che la shell possa interpolare la variabile e la necessità di citare il segno letterale del dollaro tra virgolette doppie) ma se la variabile contiene una barra, si otterrà un errore di sintassi. (La shell espande la variabile, quindi passa la stringa risultante a sed; quindi sedvede solo il testo letterale - non ha il concetto delle variabili della shell.)

La cura è usare un diverso delimitatore (dove ovviamente devi poter usare un carattere che non può comparire nel valore della variabile), ma a differenza del s%foo%bar%caso, hai anche bisogno di una barra rovesciata prima del delimitatore se vuoi usare un diverso delimitatore rispetto a quello predefinito /:

sed "\\%$variable%,\$s/foo/bar/" file

(all'interno di virgolette singole, una singola barra rovesciata sarebbe ovviamente sufficiente); oppure puoi sfuggire separatamente a ogni barra nel valore. Questa particolare sintassi è solo Bash:

sed "/${variable//\//\\/}/,\$s/foo/bar/" file

o se usi una shell diversa, prova

escaped=$(echo "$variable" | sed 's%/%\\/%g')
sed "s/$escaped/,\$s/foo/bar/" file

Per chiarezza, se $variablecontenesse la stringa, 1/2i comandi precedenti sarebbero equivalenti a

sed '\%1/2%,$s/foo/bar/' file

nel primo caso, e

sed '/1\/2/,$s/foo/bar/' file

nel secondo.


4

Usa Perl, dove le variabili sono cittadini di prima classe, non solo macro in espansione:

var=/Users/Documents/name/file perl -pe 's/\Q$ENV{var}/replace/g' $file
  • -p legge l'input riga per riga e stampa la riga dopo l'elaborazione
  • \Qcita tutti i metacaratteri nella seguente stringa (non necessario per il valore presentato qui, ma necessario se il valore contenuto [o altri valori speciali per espressioni regolari)

L'unica risposta generalmente utile a dozzine di domande sull'utilizzo di variabili nell'espressione sed in Stack Exchange. Tutto il resto è effettivamente "sfuggire a tutto ciò che è speciale di sed", che è praticamente inutile data la variabilità delle variabili e di sed.
Heath Raftery
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.