Newsgroup: it.comp.os.linux.iniziare
Titolo: sed e parametro 'g'

In sed il "pattern buffer" è ciascuna linea di input.
Il parametro "g" comporta una sostituzione globale di tutte le occorrenze
Più nel dettaglio, il parametro "g" fa sì che, dopo la sostituzione
all'interno del pattern buffer, il buffer modificato che andrebbe in output
venga copiato invece come nuovo pattern buffer. Quindi, sed itera il
comando di sostituzione su versioni sempre diverse della stessa linea
finché ogni sostituzione possibile non è stata eseguita; dopodiché passa
alla linea successiva.
Mi chiedo quanto segue.
Dato il file...
------
A;B;C;D;E
F;G;;;;I;L
M;N;O;P;Q
R;S;;U;V
1;2;3
4;5;
6;7;8
9;10;;
------
... il parametro "g" nel comando...
sed 's/[^;]*;/TEST;/g' prova.csv
... fa sì che vengano sostituiti i contenuti di tutti i campi delimitati da
un ";" con "TEST". Ma siccome il ";" fa parte sia dell'espressione regolare
(come elemento che ***non*** deve essere presente), sia della sostituzione,
come fa "g" a funzionare? Sed non dovrebbe andare in loop?
Passo 1): sostituisce A; con "TEST;"
Passo 2): copia "TEST;B;C;D;E" nel pattern buffer
Passo 3): itera sul nuovo pattern buffer l'operazione di sostituzione
ma... a questo punto non dovrebbe andare in loop iterando la sostituzione
sulla parte che precede "B;"?
==> Oppure il parametro "g" fa sì che subito dopo la sostituzione sed si
annoti l'offset, all'interno del pattern buffer modificato, dal quale
ripartire?
===> CREDO CHE LA RISPOSTA SIA PROPRIO QUEST'ULTIMA.
Inoltre, sempre dato il file "prova.csv"
------
A;B;C;D;E
F;G;;;;I;L
M;N;O;P;Q
R;S;;U;V
1;2;3
4;5;
6;7;8
9;10;;
------
supponiamo che io voglia sostituire il contenuto di ***tutti*** i campi,
vuoti o non vuoti, con TEST, compreso il campo a fine riga non terminato
da ";".
Se faccio così:
sed 's/[^;]*;/TEST;/g' prova.csv
... ovviamente restano integri i campi finali "E", "L", "Q", eccetera.
Una soluzione elegante è di usare le extended regular expression:
sed 's/[^;]*\(;\|$\)/TEST;/g' prova.csv
E se invece di ***tutti*** i campi volessi agire soltanto su quelli vuoti,
compreso il campo a fine riga non terminato da ";" ?
Io ho pensato la seguente formulazione (che usa le extended regular
expressions):
sed 's/\(;;\|;$\)/;TEST;/g' prova.csv | sed 's/\;;/;TEST;/g'
o
sed -e 's/\(;;\|;$\)/;TEST;/g' -e 's/\;;/;TEST;/g' prova.csv
... dove la "seconda passata" di editing si rende necessaria, in quanto con
solo la prima si ottiene:
------------------
A;B;C;D;E
F;G;TEST;;TEST;I;L
M;N;O;P;Q
R;S;TEST;U;V
1;2;3
4;5;TEST;
6;7;8
9;10;TEST;
------------------
... perché (per via --se suppongo bene-- di come funziona il parametro "g"),
sed, nella seconda riga, dopo il primo passaggio itera la sostituzione ***a
partire dall'offset*** e quindi non può riconoscere il quarto campo come
vuoto, passando direttamente al quindo campo (";;"), cioè all'occorrenza
che soddisfa la regular expression (e lo sostituisce con ";TEST;").
==> MI CHIEDO, comunque, se ci sia una soluzione più elegante del pipe o
dell'editing sequenziale.

[cut]

Qui c'e' uno dei problemi delle espressioni regolari usate da sed, ossia il
fatto che i match "consumano" l'input. Il problema si puo' esemplificare
semplicemente con questo:
campo1;;;campo4
e voglio cambiare tutti i campi vuoti con "TEST". Se sai gia' che non ci
saranno mai due campi vuoti consecutivi, allora 
s/;;/;TEST;/g
funziona bene. Altrimenti con sed bisogna fare due volte la stessa
sostituzione, cioe'
s/;;/;TEST;/g;s/;;/;TEST;/g
perl risolve il problema con il lookahead, e si puo' quindi fare
perl -pe 's/;(?=;)/;TEST/g' prova.csv
oppure, se vuoi anche considerare i campi vuoti non terminati da ";" (cioe'
quelli alla fine), si puo' fare
perl -pe 's/;(?=;|$)/;TEST/g' prova.csv
Ti consiglio CALDAMENTE di leggere il libro "mastering regular expressions".

 s/;;/;TEST;/g;s/;;/;TEST;/g
In realta' c'e' anche il problema del PRIMO campo vuoto (come ;campo2;campo3
ecc.), ma il concetto e' quello che ho detto.