Qual è lo scopo di utilizzare più punti esclamativi in ​​sed?


12

La documentazione POSIX sed dice:

Una funzione può essere preceduta da uno o più '!' caratteri, nel qual caso la funzione deve essere applicata se gli indirizzi non selezionano lo spazio del modello. Zero o più caratteri <blank> devono essere accettati prima del primo '!' personaggio. Non è specificato se i caratteri <blank> possano seguire un '!' carattere e le applicazioni conformi non devono seguire un '!' carattere con <blank> caratteri.

Quindi, con qualsiasi POSIX POS, possiamo:

sed -e '/pattern/!d' file

È lo stesso della scrittura:

sed -e '/pattern/!!d' file

E !!!de ndei punti esclamativi vanno ancora bene (testato con tre sedversioni dal toolchest cimelio ). Non vedo alcun vantaggio tra più anziché una esclamazione.

Perché le specifiche hanno permesso quella sintassi e come sono utili nell'applicazione nel mondo reale?


Sembra che GNU sed non sia conforme in questo caso, si lamenterà se usiamo più esclamazioni:

$ sed -e '/pattern/!!d' file
sed: -e expression #1, char 11: multiple `!'s

2
FWIW: Su OpenBSD !agisce come un interruttore, /pattern/!!è uguale a /pattern/ed /pattern/!!!è uguale a /pattern/!. Su FreeBSD i multipli !sono gli stessi di uno singolo.
lcd047,

2
Il punto di molte cose nelle specifiche è che gli sedscript possono essere generati . Dato un POSIX sed, dovrebbe essere una questione molto semplice scrivere la scrittura di una sedsceneggiatura. E quindi se avessi qualche trigger per qualche caso che dovrebbe contrassegnare un indirizzo !non degno di qualunque sia stata la tua azione, potresti persino attivarlo più volte per lo stesso e comunque ottenere gli stessi risultati.
Mikeserv,

@cuonglm No, solo FreeBSD lo è. GNU, OpenBSD e NetBSD sednon lo sono.
lcd047,

@ lcd047: sì, certo. Scusa per il mio cattivo inglese. Voglio dire, non è conforme, vero. È bello saperlo. Ma il punto principale nella mia domanda è come quella sintassi può essere utile nel mondo reale, con POSIX sed?
cuonglm,

1
FWIW: una correzione per questo è stata impegnata in OpenBSD-current.
lcd047

Risposte:


5

sedL'API è primitiva - e questo è di progettazione. Almeno, è rimasto primitivo dal design - non posso dire se sia stato progettato in modo primitivo all'inizio. Nella maggior parte dei casi, la scrittura di uno sedscript che, quando eseguito, produrrà un altro sedscript è davvero una questione semplice. sedviene molto spesso applicato in questo modo da preprocessori macro come m4e / o make.

(Quello che segue è un caso d'uso altamente ipotetico: è un problema progettato per soddisfare una soluzione. Se ti sembra un allungamento, probabilmente è perché lo è, ma ciò non lo rende necessariamente meno valido.)


Considera il seguente file di input:

cat <<"" >./infile
camel
cat dog camel
dog cat
switch
upper
lower

Se volessimo scrivere uno sedscript che aggiungesse la parola -case alla coda di ogni parola appropriata nel file di input sopra riportato solo se potesse essere trovato su una riga in un contesto appropriato , e avremmo voluto farlo nel modo più efficiente possibile ( come dovrebbe essere il nostro obiettivo, ad esempio, durante un'operazione di compilazione), allora dovremmo preferire evitare di applicare /regexp /s il più possibile.

Una cosa che potremmo fare è pre-modificare il file sul nostro sistema in questo momento e non chiamare mai seddurante la compilazione. Ma se una qualsiasi di quelle parole nel file dovesse o non dovesse essere inclusa in base alle impostazioni locali e / o alle opzioni di compilazione, probabilmente non sarebbe un'alternativa desiderabile.

Un'altra cosa che potremmo fare è elaborare il file ora contro regexps. Siamo in grado di produrre - e includere nella nostra compilation - uno sedscript che può applicare le modifiche in base al numero di riga - che in genere è un percorso molto più efficiente nel lungo periodo.

Per esempio:

n=$(printf '\\\n\t')
grep -En 'camel|upper|lower' <infile |
sed "   1i${n%?}#!/usr/heirloom/bin/posix2001/sed -nf
        s/[^:]*/:&$n&!n;&!b&$n&/;s/://2;\$a${n%?}q"'
        s/ *cat/!/g;s/ *dog/!/g
        s| *\([cul][^ ]*\).*|s/.*/\1-case/p|'

... che scrive l'output sotto forma di uno sedscript e che sembra ...

#!/usr/heirloom/bin/posix2001/sed -nf
:1
    1!n;1!b1
    1s/.*/camel-case/p
:2
    2!n;2!b2
    2!!s/.*/camel-case/p
:5
    5!n;5!b5
    5s/.*/upper-case/p
:6
    6!n;6!b6
    6s/.*/lower-case/p
q

Quando quell'output viene salvato in un file di testo eseguibile sul mio computer chiamato ./bang.seded eseguito come ./bang.sed ./infile, l'output è:

camel-case
upper-case
lower-case

Ora potresti chiedermi ... Perché dovrei farlo? Perché non dovrei semplicemente ancorare greple partite? Chi usa comunque la custodia per cammelli? E a ogni domanda a cui ho potuto solo rispondere, non ho idea ... perché non lo so. Prima di leggere questa domanda non avevo mai notato personalmente il multi-! requisito di analisi nelle specifiche - Penso che sia una cattura piuttosto ordinata.

Il multi-! la cosa ha immediatamente avuto un senso per me, però - gran parte delle sedspecifiche è orientata verso script semplicemente analizzati e semplicemente generati sed . Probabilmente troverai i \ndelimitatori di ewline richiesti per [wr:bt{]avere molto più senso in quel contesto, e se tieni a mente quell'idea potresti dare un senso migliore ad alcuni altri aspetti delle specifiche - (come l' :accettazione di indirizzi e il qrifiuto di accetta non più di 1) .

Nell'esempio di cui sopra che scrivo una certa forma di sedscript che può solo sempre essere letto una volta. Se lo guardi con attenzione, potresti notare che mentre sedlegge il file di modifica passa da un blocco di comandi al successivo - non si discosta mai o completa il suo script di modifica fino a quando non è completamente finito con il suo file di modifica.

Lo considero multi-! gli indirizzi potrebbero essere più utili in quel contesto che in alcuni altri, ma, onestamente, non riesco a pensare a un singolo caso in cui avrei potuto sfruttarlo molto bene - e io sedmolto. Penso anche che sia degno di nota che sedentrambi GNU / BSD non riescano a gestirlo come specificato - questo probabilmente non è un aspetto della specifica che è molto richiesto, e quindi se un'implementazione lo trascura dubito molto seriamente che i loro bug @ box ne soffriranno di conseguenza terribilmente.

Detto questo, la mancata gestione di questo come specificato è un bug per qualsiasi implementazione che pretende di essere conforme, e quindi penso che sia necessario sparare un'e-mail alle relative caselle di sviluppo qui, e intendo farlo in caso contrario.


1
Ora è stato corretto in OpenBSD-current.
lcd047

1
Il multiplo !verrà rimosso nelle prossime specifiche , cosa sta succedendo qui!
cuonglm,

@cuonglm - troppo poco, troppo tardi, immagino. forse ero più vicino al segno di quanto pensassi.
Mikeserv,

@cuonglm - bene, ok, ma cosa significa ... Accettato come contrassegnato significa anche?
Mikeserv,

1
@mikeserv: la risposta ha spiegato la mia meraviglia e mi ha dato un'altra visione con API sed. Ha senso per me!
cuonglm,
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.