Numero di barre rovesciate necessarie per sfuggire alla barra rovesciata regex sulla riga di comando


12

Di recente ho avuto problemi con alcuni regex sulla riga di comando e ho scoperto che per abbinare una barra rovesciata, è possibile utilizzare diversi numeri di caratteri. Questo numero dipende dalla quotazione utilizzata per la regex (nessuna, virgolette singole, virgolette doppie). Vedi la seguente sessione bash per cosa intendo:

echo "#ab\\cd" > file
grep -E ab\cd file
grep -E ab\\cd file
grep -E ab\\\cd file
grep -E ab\\\\cd file
#ab\cd
grep -E ab\\\\\cd file
#ab\cd
grep -E ab\\\\\\cd file
#ab\cd
grep -E ab\\\\\\\cd file
#ab\cd
grep -E ab\\\\\\\\cd file
grep -E "ab\cd" file
grep -E "ab\\cd" file
grep -E "ab\\\cd" file
#ab\cd
grep -E "ab\\\\cd" file
#ab\cd
grep -E "ab\\\\\cd" file
#ab\cd
grep -E "ab\\\\\\cd" file
#ab\cd
grep -E "ab\\\\\\\cd" file
grep -E 'ab\cd' file
grep -E 'ab\\cd' file
#ab\cd
grep -E 'ab\\\cd' file
#ab\cd
grep -E 'ab\\\\cd' file

Ciò significa che:

  • senza virgolette, posso abbinare una barra rovesciata con 4-7 barre rovesciate effettive
  • con le virgolette doppie, posso abbinare una barra rovesciata con 3-6 barre rovesciate effettive
  • Con virgolette singole, posso abbinare una barra rovesciata con 2-3 barre rovesciate effettive

Capisco che una barra rovesciata in più viene ignorata dalla shell (dalla pagina man di bash):

"Una barra rovesciata non quotata (\) è il carattere di escape. Conserva il valore letterale del carattere successivo che segue"

Questo non si applica agli esempi con virgolette singole, poiché non viene eseguita alcuna escape tra virgolette singole.

E una barra rovesciata aggiuntiva viene ignorata dal comando grep ("\ c" è solo "c" evaso, ma questo è lo stesso di "c", perché "c" non ha un significato speciale in una regex).

Questo spiega il comportamento dell'esempio con virgolette singole, ma in realtà non capisco gli altri due esempi, soprattutto perché c'è una differenza tra stringhe non qouted e doppie tra virgolette.

Ancora una volta, una citazione dalla pagina man di bash:

"Racchiudere i caratteri tra virgolette doppie conserva il valore letterale di tutti i caratteri tra virgolette, ad eccezione di $,`, \ e, quando l'espansione della cronologia è abilitata,!. "

Ho provato lo stesso con GNU awk (ad esempio awk /ab\cd/{print} file), con gli stessi risultati.

Perl, tuttavia, mostra risultati diversi (usando ad esempio perl -ne "/ab\\cd/"\&\&print file):

  • senza virgolette, posso abbinare una barra rovesciata con 4-5 barre rovesciate effettive
  • con le virgolette doppie, posso abbinare una barra rovesciata con 3-4 barre rovesciate effettive
  • Con virgolette singole, posso associare una barra rovesciata a 2 barre rovesciate effettive

Qualcuno può spiegare quella differenza tra stringhe regex non quotate e double qouted sulla riga di comando per grep e awk? Non sono così interessato a una spiegazione del comportamento di Perl, dal momento che di solito non uso le battute Perl.

Risposte:


10

Per l'esempio non quotato, ogni \\coppia passa una barra rovesciata a grep, quindi 4 barre rovesciate due passano a grep, il che si traduce in una singola barra rovesciata. 6 barre rovesciate passano da tre a grep, traducendosi in una barra rovesciata e una \c, che è uguale a c. Una barra rovesciata aggiuntiva non cambia nulla, perché viene tradotta \c-> cdalla shell. Otto barre rovesciate nella shell sono quattro in grep, tradotte in due, quindi questo non corrisponde più.

Per l'esempio tra virgolette doppie, nota ciò che segue la tua seconda citazione dalla manpage di bash:

La barra rovesciata conserva il suo significato speciale solo quando è seguita da uno dei seguenti caratteri: $, `,", \ o newline.

Cioè quando si dà un numero dispari di barre rovesciate, la sequenza termina in \c, che sarebbe uguale a cnel caso non quotato, ma quando viene quotata, la barra rovesciata perde il suo significato speciale, quindi \cviene passata a grep. Questo è il motivo per cui l'intervallo di "possibili" barre rovesciate (ovvero quelle che compongono uno schema corrispondente al tuo file di esempio) scende di uno.


... e poi ci sono alcune stranezze: per esempio: printf "\ntest"inserirà una nuova riga prima di "test", anche se "\n"avrebbe dovuto essere tradotto "n"dalla shell in quanto è racchiuso tra virgolette ... (quindi il risultato atteso dovrebbe essere, per "\ ntest", "ntest". Dovremmo prendere l'abitudine di scrivere: printf "\\ntest"o printf '\ntest', ma in qualche modo vedo molte sceneggiature che fanno affidamento sulla stranezza.
Olivier Dulac,

6

Questo link descriveva bash Quotes and Escaping

La tua domanda riguarda le prime tre sezioni.

  • Fuga per personaggio
  • Citazioni deboli "virgolette doppie"
  • Citazioni forti "virgolette singole"
  • ANSI C come la citazione delle stringhe
  • Quotazione I18N / L10N (internazionalizzazione e localizzazione) .

Di seguito è riportato un grafico di come le stringhe le bashpassano grepe come le grepinterpreta ulteriormente internamente.

Diamo una prima occhiata a echo "#ab\\cd" > file.
Nel punto debole ("") "#ab\\cd", \\è un escape \che viene passato filecome un singolo letterale \. Quindi, filecontiene ab\cd

Ora, ai tuoi comandi: la tabella qui sotto può aiutarti a vedere cosa succede realmente con ogni chiamata. La *mostra quelle che corrispondono il contenuto del file. In realtà si tratta solo di applicare le regole di fuga di bash, come nella pagina web, con particolare attenzione alla risposta di Daniel Kullmann in cui si riferisce al comportamento di fuga in una situazione di citazioni deboli .

La barra rovesciata conserva il suo significato speciale solo quando è seguita da uno dei seguenti caratteri: $, `,", \ o newline.


                            bash passes    grep further
                            to grep        resolves to         
grep -E ab\cd file            abcd           abcd   
grep -E ab\\cd file           ab\cd          abcd  
grep -E ab\\\cd file          ab\cd          abcd
grep -E ab\\\\cd file         ab\\cd         ab\cd    * 
grep -E ab\\\\\cd file        ab\\\cd        ab\cd    *
grep -E ab\\\\\\cd file       ab\\\cd        ab\cd    *    
grep -E ab\\\\\\\cd file      ab\\\cd        ab\cd    *
grep -E ab\\\\\\\\cd file     ab\\\\cd       ab\\cd

grep -E "ab\cd" file          ab\cd          abcd
grep -E "ab\\cd" file         ab\cd          abcd
grep -E "ab\\\cd" file        ab\\cd         ab\cd    *
grep -E "ab\\\\cd" file       ab\\cd         ab\cd    *
grep -E "ab\\\\\cd" file      ab\\\cd        ab\cd    *
grep -E "ab\\\\\\cd" file     ab\\\cd        ab\cd    *
grep -E "ab\\\\\\\cd" file    ab\\\\cd       ab\\cd    

grep -E 'ab\cd' file          ab\cd          abcd  
grep -E 'ab\\cd' file         ab\\cd         ab\cd    *
grep -E 'ab\\\cd' file        ab\\\cd        ab\cd    *
grep -E 'ab\\\\cd' file       ab\\\\cd       ab\\cd
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.