Questa è una domanda del tutto banale.
Questo funziona sostituendo il primo spazio interno citazioni con sottolineatura:
$ sed 's/\("[^ "]*\) \([^"]*"\)/\1_\2/g' f.txt
"a_aa" MM "bbb_ b"
MM MM
MM"b_b "
$
Per questo esempio, dove non ci sono più di due spazi all'interno di qualsiasi delle citazioni, si è tentati di ripetere semplicemente il comando, ma dà un risultato errato:
$ sed -e 's/\("[^ "]*\) \([^"]*"\)/\1_\2/g' \
> -e 's/\("[^ "]*\) \([^"]*"\)/\1_\2/g' f.txt
"a_aa"_ MM "bbb_ b"
MM MM
MM"b_b_"
$
Se la versione di sed
supporta 'esteso espressioni regolari', allora questo funziona per i dati di esempio:
$ sed -E \
> -e 's/^(([^"]*("[^ "]*")?)*)("[^ "]*) ([^"]*")/\1\4_\5/' \
> -e 's/^(([^"]*("[^ "]*")?)*)("[^ "]*) ([^"]*")/\1\4_\5/' \
> -e 's/^(([^"]*("[^ "]*")?)*)("[^ "]*) ([^"]*")/\1\4_\5/' \
> f.txt
"a_aa" MM "bbb__b"
MM MM
MM"b_b_"
$
È necessario ripetere questa espressione orribile per ogni spazio tra virgolette doppie, quindi tre volte per la prima riga di dati.
l'espressione regolare può essere spiegato come:
- A partire dall'inizio di una riga,
- Cercare sequenze di 'zero o più non-citazioni, eventualmente seguito da una citazione, senza spazi o virgolette , e una citazione ', l'intero assembly ripetuto zero o più volte,
- Seguito da una citazione, zero o più non citazioni, non spazi, uno spazio e zero o più non citazioni e una citazione.
- Sostituire il materiale adattato con la parte anteriore, il materiale all'inizio del passaggio quotato corrente, un trattino basso e il materiale finale del passaggio quotato corrente.
causa dell'ancora partenza, questa deve essere ripetuta una volta al vuoto ... ma sed
ha un costrutto iterativo, in modo che possiamo farlo con:
$ sed -E -e ':redo
> s/^(([^"]*("[^ "]*")?)*)("[^ "]*) ([^"]*")/\1\4_\5/
> t redo' f.txt
"a_aa" MM "bbb__b"
MM MM
MM"b_b_"
$
Il :redo
definisce un'etichetta; il comando s///
è come prima; il comando t redo
salta all'etichetta se è stata eseguita una sostituzione dall'ultima lettura di una linea o passando a un'etichetta.
Data la discussione nei commenti, ci sono un paio di punti degni di nota:
L'opzione -E
applica a sed
su MacOS X (testato 10.7.2).L'opzione corrispondente per la versione GNU di sed
è -r
(o --regex-extended
). L'opzione -E
è coerente con grep -E
(che utilizza anche le espressioni regolari estese). I "sistemi Unix classici" non supportano ERE con sed
(Solaris 10, AIX 6, HP-UX 11).
è possibile sostituire il ?
ho usato (che è l'unico personaggio che impone l'utilizzo di un ERE invece di un BRE) con *
, e poi trattare con le parentesi (che richiedono backslash davanti a loro in un BRE per trasformarli in parentesi cattura), lasciando lo script:
sed -e ':redo
s/^\(\([^"]*\("[^ "]*"\)*\)*\)\("[^ "]*\) \([^"]*"\)/\1\4_\5/g
t redo' f.txt
Ciò produce lo stesso output sullo stesso input - ho provato alcuni modelli leggermente più complessi in ingresso:
"a aa" MM "bbb b"
MM MM
MM"b b "
"c c""d d""e e" X " f "" g "
"C C" "D D" "E E" x " F " " G "
Thi s dà l'output:
"a_aa" MM "bbb__b"
MM MM
MM"b_b_"
"c_c""d_d""e__e" X "_f_""_g_"
"C_C" "D_D" "E__E" x "_F_" "_G_"
Anche con BRE notazione, sed
supportata la notazione \{0,1\}
per specificare 0 o 1 occorrenze del termine RE precedente, così la versione ?
potrebbe tradurre in una BRE utilizzando:
sed -e ':redo
s/^\(\([^"]*\("[^ "]*"\)\{0,1\}\)*\)\("[^ "]*\) \([^"]*"\)/\1\4_\5/g
t redo' f.txt
Questo produce la stessa uscita delle altre alternative.
fonte
2011-11-25 07:52:41
BTW: buona domanda, specialmente con l'input di esempio e l'output richiesto. –