È possibile adottare approcci diversi a seconda che si awk
tratti RS
di un singolo carattere (come awk
fanno le implementazioni tradizionali ) o di un'espressione regolare (simile gawk
o simile mawk
). Anche i file vuoti sono difficili da considerare perché awk
tendono a saltarli.
gawk
, mawk
O altre awk
implementazioni in cui RS
possono essere un espressione regolare.
In quelle implementazioni (per mawk
, attenzione che alcuni sistemi operativi come Debian forniscono una versione molto vecchia invece di quella moderna gestita da @ThomasDickey ), se RS
contiene un singolo carattere, il separatore di record è quel personaggio o awk
entra nella modalità paragrafo quando RS
è vuoto, o considera RS
altrimenti un'espressione regolare.
La soluzione è quella di utilizzare un'espressione regolare che non può essere abbinata. Alcuni vengono in mente come x^
o $x
( x
prima dell'inizio o dopo la fine). Tuttavia alcuni (in particolare con gawk
) sono più costosi di altri. Finora ho scoperto che ^$
è il più efficiente. Può corrispondere solo su un input vuoto, ma non ci sarebbe nulla con cui confrontarsi.
Quindi possiamo fare:
awk -v RS='^$' '{printf "%s: <%s>\n", FILENAME, $0}' file1 file2...
Un avvertimento però è che salta i file vuoti (contrariamente a perl -0777 -n
). Ciò può essere risolto con GNU awk
inserendo invece il codice in ENDFILE
un'istruzione. Ma dobbiamo anche ripristinare $0
in un'istruzione BEGINFILE in quanto altrimenti non verrebbe ripristinato dopo l'elaborazione di un file vuoto:
gawk -v RS='^$' '
BEGINFILE{$0 = ""}
ENDFILE{printf "%s: <%s>\n", FILENAME, $0}' file1 file2...
awk
implementazioni tradizionali , POSIXawk
In quelli, RS
è solo un personaggio, non hanno BEGINFILE
/ ENDFILE
, non hanno la RT
variabile, inoltre generalmente non possono elaborare il carattere NUL.
Si potrebbe pensare che l'utilizzo RS='\0'
possa funzionare, dato che comunque non possono elaborare input che contiene il byte NUL, ma no, che RS='\0'
nelle implementazioni tradizionali viene trattato come RS=
, che è la modalità paragrafo.
Una soluzione può essere quella di utilizzare un personaggio che difficilmente si trova nell'input come \1
. Nelle localizzazioni di caratteri multibyte, puoi persino renderle sequenze di byte che è molto improbabile che si verifichino poiché formano caratteri che non sono assegnati o non caratteri come $'\U10FFFE'
nelle localizzazioni UTF-8. Non è assolutamente infallibile e hai anche un problema con i file vuoti.
Un'altra soluzione può essere quella di memorizzare l'intero input in una variabile e di elaborarlo nell'istruzione END alla fine. Ciò significa che è possibile elaborare solo un file alla volta:
awk '{content = content $0 RS}
END{$0 = content
printf "%s: <%s>\n", FILENAME, $0
}' file
Questo è l'equivalente di sed
:
sed '
:1
$!{
N;b1
}
...' file1
Un altro problema con questo approccio è che se il file non terminava con un carattere di nuova riga (e non era vuoto), $0
alla fine ne viene aggiunto uno arbitrariamente (con gawk
, si aggira il problema usando RT
invece che RS
nel codice sopra). Un vantaggio è che hai un record del numero di righe nel file in NR
/ FNR
.