Perché è `sed expr1 | sed expr2` diverso da `sed -e expr1 -e expr2`


10

Dividevo l'output da idper fornire un elenco più leggibile riga per elenco di gruppi di cui un utente è membro:

id roaima | sed 's/,/\n\t/g'
uid=1001(roaima) gid=1001(roaima) groups=1001(roaima)
    24(cdrom)
    25(floppy)
    ...
    822413650 (international (uk) location)

Volevo separare il numero del gruppo dal nome tra parentesi, quindi ho esteso l'espressione in questo modo

id roaima | sed -e 's/,/\n\t/g' -e '2,$s/(/ (/'

Tuttavia, questo non ha funzionato come inizialmente mi aspettavo. La seconda espressione sembrava non avere alcun effetto.

Invece, per ottenere il risultato che volevo, dovevo eseguire due sedcomandi separati , come questo:

id roaima | sed -e 's/,/\n\t/g' | sed '2,$s/(/ (/'
uid=1001(roaima) gid=1001(roaima) groups=1001(roaima)
    24 (cdrom)
    25 (floppy)
    ...
    822413650 (international (uk) location)

Perché ho bisogno di due sedcomandi in una pipe anziché uno con più istruzioni? O se posso farlo con uno sed, come lo farei?

Quello che mi piacerebbe in particolare è avere il singolo spazio tra il valore UID / GID e il suo nome tra parentesi per ogni singolo elemento (inclusi UID e GID sulla prima riga), ma l'avvertenza è che nei miei dati reali posso avere gruppi contenenti parentesi nei loro nomi e non voglio che i nomi stessi vengano alterati.

Risposte:


14

sed, come awko cuto perl -nefunziona su ciascuna riga singolarmente una dopo l'altra.

sed -e code1 -e code2

viene effettivamente eseguito come:

while(patternspace = getline()) {
  linenumber++
  code1
  code2
} continue {print patternspace}

Se il tuo codice2 è 2,$ s/foo/bar/, questo è:

if (linenumber >= 2) sub(/foo/, "bar", patternspace)

Poiché il tuo input ha solo una riga, sub()non verrà mai eseguito.

L'inserimento di caratteri di nuova riga nello spazio modello in code1non linenumberaumenta.

Invece, hai uno spazio modello con più righe mentre elabora la prima e unica riga di input. Se si desidera apportare modifiche sulla seconda riga e oltre di quello spazio del modello multilinea, è necessario eseguire qualcosa del tipo:

s/\(\n[^(]*\)(/\1 (/g

Anche se qui, ovviamente, potresti anche fare le due operazioni in una volta sola:

id | sed 's/,\([^(]*\)(/\n\t\1 (/g'

awk, e perl -n / p, lavora su ogni record che per impostazione predefinita è una riga ma può essere modificato; in questo caso -vRS=,o -054potrebbe aiutare.
dave_thompson_085,

5

Se hai GNU sed, puoi usarlo

id username | sed 's/(/ (/4g; s/,/\n\t/g'

che aggiunge uno spazio prima della quarta e successiva parentesi aperta, quindi sostituisce le virgole.


1
Sembra interessante. Sfortunatamente influisce anche sui nomi di gruppo che contengono parentesi come il mio esempio, international (uk) locationinserendo uno spazio indesiderato nel nome stesso.
roaima,

Quindi utilizzare s/\([[:digit:]]\+\)(/\1 (/4gciò che aggiungerà uno spazio solo se ci sono cifre prima della parentesi.
Glenn Jackman,

1

Ciò che ha detto @ stéphane-chazelas è vero, ma puoi sempre aggiungere prima lo spazio e dividere in linee dopo in questo modo:

sed -e 's:\([,=][0-9]*\):\1 :g' -e 's:,:\n\t:g'

O in un singolo script sed (senza -e):

sed 's:\([,=][0-9]*\):\1 :g; s:,:\n\t:g'

Normalmente usiamo " /" come separatore di ricerca / i di comando, ma accetta anche qualsiasi carattere, quindi a volte è più facile leggere usando altri caratteri come " :" per evitare combinazioni come " /\".

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.