Come garantire che la stringa interpolata nella sostituzione `sed` sfugga a tutti i metachar


21

Ho uno script che legge un flusso di testo e genera un file di comandi sed che verrà successivamente eseguito con sed -f. I comandi sed generati sono come:

s/cid:image002\.gif@01CC3D46\.926E77E0/https:\/\/mysite.com\/files\/1922/g
s/cid:image003\.gif@01CC3D46\.926E77E0/https:\/\/mysite.com\/files\/1923/g
s/cid:image004\.jpg@01CC3D46\.926E77E0/https:\/\/mysite.com\/files\/1924/g

Supponiamo che lo script che genera i sedcomandi sia qualcosa del tipo:

while read cid fileid
do
    cidpat="$(echo $cid | sed -e s/\\./\\\\./g)"
    echo 's/'"$cidpat"'/https:\/\/mysite.com\/files\/'"$fileid"'/g' >> sedscr
done

Come posso migliorare lo script per garantire che tutti i metacaratteri di regex nella cidstringa siano salvati e interpolati correttamente?

Risposte:


24

Per sfuggire alle variabili da usare sul lato sinistro e sul lato destro di un scomando sed(qui $lhse $rhsrispettivamente), dovresti fare:

escaped_lhs=$(printf '%s\n' "$lhs" | sed 's:[][\/.^$*]:\\&:g')
escaped_rhs=$(printf '%s\n' "$rhs" | sed 's:[\/&]:\\&:g;$!s/$/\\/')

sed "s/$escaped_lhs/$escaped_rhs/"

Si noti che $lhsnon può contenere un carattere di nuova riga.

Cioè, su LHS, sfuggire a tutti gli operatori regexp ( ][.^$*), al carattere di escape stesso ( \) e al separatore ( /).

Sull'RHS, devi solo scappare &, il separatore, la barra rovesciata e il carattere di nuova riga (cosa che fai inserendo una barra rovesciata alla fine di ogni riga tranne l'ultima ( $!s/$/\\/)).

Ciò presuppone che tu usi /come separatore nei tuoi sed scomandi e che non abiliti le RE estese con -r(GNU sed/ ssed/ ast/ busybox sed) o -E(BSD, astGNU recente, recente scatola occupata ) o PCRE con -R( ssed) o le RE aumentate con -A/ -X( ast) che tutti hanno operatori RE extra.

Alcune regole di base quando si tratta di dati arbitrari:

  • Non usare echo
  • cita le tue variabili
  • considerare l'impatto della locale (in particolare il suo set di caratteri: è importante che i comandi di escape sed siano eseguiti nella stessa locale del sedcomando usando ad esempio le stringhe di escape (e con lo stesso sedcomando))
  • non dimenticare il carattere di nuova riga (qui potresti voler controllare se ne $lhscontiene qualcuno e agire).

Un'altra opzione è quella di utilizzare perlinvece sede passare le stringhe nell'ambiente e utilizzare gli operatori \Q/ \E perlregexp per prendere letteralmente le stringhe:

A="$lhs" B="$rhs" perl -pe 's/\Q$ENV{A}\E/$ENV{B}/g'

perl(per impostazione predefinita) non sarà influenzato dal set di caratteri della locale poiché, in quanto sopra, considera solo le stringhe come matrici di byte senza preoccuparsi di quali caratteri (se presenti) possono rappresentare per l'utente. Con sed, è possibile ottenere lo stesso risultato fissando la locale su Ccon LC_ALL=Cper tutti i sedcomandi (anche se ciò influirà anche sulla lingua dei messaggi di errore, se presenti).


Cosa succede se devo evitare le doppie virgolette?
Menon,

@Menon, le doppie virgolette non sono speciali sed, non è necessario sfuggirle.
Stéphane Chazelas,

Questo non può essere usato per la corrispondenza dei pattern usando i caratteri jolly, vero?
Menon,

@Menon, no, la corrispondenza dei caratteri jolly come con find's -nameè diversa dalle espressioni regolari. Lì devi solo scappare ?, *rovesciare e[
Stéphane Chazelas
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.