Możesz zastosować różne podejścia w zależności od tego, czy awk
traktuje się RS
jako pojedynczy znak (jak awk
robią to tradycyjne implementacje), czy jako wyrażenie regularne (jak gawk
lub mawk
robią). Puste pliki są również trudne do rozważenia, ponieważ awk
zwykle je pomijają.
gawk
, mawk
Lub inne awk
implementacje, gdzie RS
może być wyrażeniem regularnym.
W tych implementacjach (na przykład mawk
, uważaj, że niektóre systemy operacyjne, takie jak Debian, dostarczają bardzo starą wersję zamiast nowoczesnej obsługiwanej przez @ThomasDickey ), jeśli RS
zawiera pojedynczy znak, separatorem rekordów jest ten znak lub awk
wchodzi w tryb akapitowy, gdy RS
jest pusty, lub inaczej traktuje RS
jako wyrażenie regularne.
Rozwiązaniem jest użycie wyrażenia regularnego, którego nie można dopasować. Niektórzy przychodzą na myśl jak x^
lub $x
( x
przed rozpoczęciem lub po zakończeniu). Jednak niektóre (szczególnie z gawk
) są droższe niż inne. Jak dotąd uważam, że ^$
jest to najbardziej wydajny. Można dopasować tylko przy pustym wejściu, ale wtedy nie byłoby nic, z czym można by się dopasować.
Więc możemy zrobić:
awk -v RS='^$' '{printf "%s: <%s>\n", FILENAME, $0}' file1 file2...
Jedynym zastrzeżeniem jest to, że pomija puste pliki (w przeciwieństwie do perl -0777 -n
). Można to rozwiązać za pomocą GNU awk
, umieszczając kod w ENDFILE
instrukcji. Ale musimy również zresetować $0
w instrukcji BEGINFILE, ponieważ w przeciwnym razie nie zostałby zresetowany po przetworzeniu pustego pliku:
gawk -v RS='^$' '
BEGINFILE{$0 = ""}
ENDFILE{printf "%s: <%s>\n", FILENAME, $0}' file1 file2...
tradycyjne awk
wdrożenia, POSIXawk
W nich RS
jest tylko jeden znak, nie mają BEGINFILE
/ ENDFILE
, nie mają RT
zmiennej, ogólnie też nie mogą przetwarzać znaku NUL.
Można by pomyśleć, że użycie RS='\0'
może wtedy działać, ponieważ i tak nie mogą przetwarzać danych wejściowych zawierających bajt NUL, ale nie, że RS='\0'
w tradycyjnych implementacjach jest traktowane jako RS=
tryb akapitowy.
Jednym z rozwiązań może być użycie znaku, który prawdopodobnie nie znajdzie się w danych wejściowych, takich jak \1
. W lokalizacjach znaków wielobajtowych możesz nawet sprawić, że sekwencje bajtów będą bardzo mało prawdopodobne, ponieważ tworzą one znaki, które nie są przypisane lub znaki inne niż $'\U10FFFE'
w ustawieniach regionalnych UTF-8. Nie bardzo niezawodny i masz również problem z pustymi plikami.
Innym rozwiązaniem może być przechowywanie całego wejścia w zmiennej i przetwarzanie go w instrukcji END na końcu. Oznacza to jednak, że możesz przetwarzać tylko jeden plik na raz:
awk '{content = content $0 RS}
END{$0 = content
printf "%s: <%s>\n", FILENAME, $0
}' file
To odpowiednik sed
:
sed '
:1
$!{
N;b1
}
...' file1
Kolejny problem z tym podejściem jest to, że jeśli plik nie kończy się znakiem nowej linii (i nie była pusta), jeden jest wciąż arbitralnie dodane $0
na końcu (z gawk
, można obejść, że stosując RT
zamiast RS
w kod powyżej). Jedną z zalet jest to, że masz rekord liczby linii w pliku w NR
/ FNR
.