Ho uno script che legge un flusso di testo e genera un file di comandi sed che viene successivamente eseguito con sed -f
. I comandi sed generati sono come:
s/cid:[email protected]/https://mysite.com/files/1922/g
s/cid:[email protected]/https://mysite.com/files/1923/g
s/cid:[email protected]/https://mysite.com/files/1924/g
Assumere lo script che genera il sed
comandi è 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 tutti i metacaratteri regex nel cid
le stringhe sono state salvate e interpolate correttamente?
Risposta accettata:
Per sfuggire alle variabili da utilizzare sul lato sinistro e destro di una s
comando in sed
(qui $lhs
e $rhs
rispettivamente), faresti:
escaped_lhs=$(printf '%sn' "$lhs" | sed 's:[][\/.^$*]:\&:g')
escaped_rhs=$(printf '%sn' "$rhs" | sed 's:[\/&]:\&:g;$!s/$/\/')
sed "s/$escaped_lhs/$escaped_rhs/"
Nota che $lhs
non può contenere un carattere di nuova riga.
Cioè, sull'LHS, esegui l'escape di tutti gli operatori regexp (][.^$*
), il carattere di escape stesso ( ) e il separatore (
/
).
Su RHS, devi solo sfuggire a &
, il separatore, la barra rovesciata e il carattere di nuova riga (che si esegue inserendo una barra rovesciata alla fine di ogni riga eccetto l'ultima ($!s/$/\/
)).
Ciò presuppone che tu usi /
come separatore nel tuo sed
s
comandi e che non abiliti le RE estese con -r
(GNU sed
/ssed
/ast
/busybox sed
) o -E
(BSD, ast
, GNU recente, Busybox recente) o PCRE con -R
(ssed
) o RE aumentate con -A
/-X
(ast
) che hanno tutti operatori RE aggiuntivi.
Alcune regole di base quando si tratta di dati arbitrari:
- Non utilizzare
echo
- cita le tue variabili
- considerare l'impatto delle impostazioni locali (in particolare il suo set di caratteri:è importante che l'scappare
sed
i comandi vengono eseguiti nella stessa locale dised
comando usando il escaped stringhe (e con lo stessosed
comando) per esempio) - non dimenticare il carattere di nuova riga (qui potresti voler controllare se
$lhs
contiene qualsiasi e intraprendere un'azione).
Un'altra opzione è usare perl
invece di sed
e passa le stringhe nell'ambiente e usa il Q
/E
perl
operatori regexp per prendere le stringhe alla lettera:
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 le stringhe solo come array di byte senza preoccuparsi di quali caratteri (se presenti) possono rappresentare per l'utente. Con sed
, potresti ottenere lo stesso impostando la locale su C
con LC_ALL=C
per tutti i sed
comandi (sebbene ciò influirà anche sulla lingua dei messaggi di errore, se presenti).