awk 'processing_script_here' my=file.txt
wydaje się zatrzymywać i czekać w nieskończoność ...
Co się tutaj dzieje i jak mam to zrobić?
awk 'processing_script_here' my=file.txt
wydaje się zatrzymywać i czekać w nieskończoność ...
Co się tutaj dzieje i jak mam to zrobić?
Odpowiedzi:
Jak mówi Chris , argumenty formularza variablename=anything
są traktowane jako przypisanie zmiennej (wykonywane w momencie przetwarzania argumentów, w przeciwieństwie do (nowszych) -v var=value
, które są wykonywane przed BEGIN
instrukcjami) zamiast nazw plików wejściowych.
Może to być przydatne w takich przypadkach jak:
awk '{print $1}' FS=/ RS='\n' file1 FS='\n' RS= file2
Gdzie możesz określić inny FS
/ RS
na plik. Jest również powszechnie stosowany w:
awk '!file1_processed{a[$0]; next}; {...}' file1 file1_processed=1 file2
Która jest bezpieczniejszą wersją:
awk 'NR==FNR{a[$0]; next}; {...}' file1 file2
(co nie działa, jeśli file1
jest puste)
Ale to przeszkadza, gdy masz pliki, których nazwa zawiera =
znaki.
To tylko problem, gdy resztą pierwszego =
jest poprawna awk
nazwa zmiennej.
To, co stanowi prawidłową nazwę zmiennej w, awk
jest surowsze niż w sh
.
POSIX wymaga czegoś takiego jak:
[_a-zA-Z][_a-zA-Z0-9]*
Tylko znaki z przenośnego zestawu znaków. Jednak /usr/xpg4/bin/awk
Solaris 11 przynajmniej nie jest zgodny w tym względzie i dopuszcza wszelkie znaki alfabetyczne w ustawieniach regionalnych w nazwach zmiennych, nie tylko a-zA-Z.
Zatem argument taki jak x+y=foo
lub =bar
lub ./foo=bar
jest nadal traktowany jako nazwa pliku wejściowego, a nie przypisanie, ponieważ to, co pozostało z pierwszego, =
nie jest prawidłową nazwą zmiennej. Argument taki Stéphane=Chazelas.txt
może, ale nie musi, w zależności od awk
implementacji i ustawień regionalnych.
Dlatego w awk zaleca się stosowanie:
awk '...' ./*.txt
zamiast
awk '...' *.txt
na przykład, aby uniknąć problemu, jeśli nie możesz zagwarantować, że nazwa txt
plików nie będzie zawierać =
znaków.
Pamiętaj też, że taki argument -vfoo=bar.txt
może być traktowany jako opcja, jeśli używasz:
awk -f file.awk -vfoo=bar.txt
(Dotyczy to również awk '{code}' -vfoo=bar.txt
z awk
od wersji BusyBox przed 1.28.0, patrz odpowiadający raport o błędzie ).
Ponownie, użycie ./*.txt
działa w ten sposób (użycie ./
przedrostka pomaga również w wywołaniu pliku, -
który inaczej awk
rozumie się jako oznaczający standardowe wejście ).
Właśnie dlatego
#! /usr/bin/awk -f
sekstensy naprawdę nie działają. Podczas gdy var=value
te można obejść poprzez ustalenie tych ARGV
wartości (dodać ./
przedrostek) w BEGIN
oświadczeniu:
#! /usr/bin/awk -f
BEGIN {
for (i = 1; i < ARGC; i++)
if (ARGV[i] ~ /^[_[:alpha:]][_[:alnum:]]*=/)
ARGV[i] = "./" ARGV[i]
}
# rest of awk script
To nie pomoże w przypadku opcji, ponieważ są one widziane przez skrypt, awk
a nie przez niego awk
.
Jednym z potencjalnych problemów kosmetycznych z użyciem tego ./
prefiksu jest to, że się kończy FILENAME
, ale zawsze możesz substr(FILENAME, 3)
go rozebrać, jeśli nie chcesz.
Implementacja GNU awk
naprawia wszystkie te problemy z jej -E
opcją.
Po tym -E
gawk oczekuje tylko ścieżki awk
skryptu (gdzie -
wciąż oznacza stdin), a następnie tylko listy ścieżek do pliku wejściowego (i tam nawet nie -
jest specjalnie traktowany).
Jest specjalnie zaprojektowany dla:
#! /usr/bin/gawk -E
shebangs, w których lista argumentów jest zawsze plikami wejściowymi (pamiętaj, że nadal możesz edytować tę ARGV
listę w BEGIN
instrukcji).
Możesz także użyć go jako:
gawk -e '...awk code here...' -E /dev/null *.txt
Używamy -E
z pustym skryptem ( /dev/null
), aby upewnić się, że *.txt
później będą zawsze traktowane jako pliki wejściowe, nawet jeśli zawierają =
znaki.
../foo
, /path/to/foo
i ścieżki, które są w innym kodowaniu) - w którym to przypadku substr(FILENAME,3)
nie wystarczy, lub skrypt z jednym strzałem, w którym użytkownik w zasadzie zna nazwy plików - w takim przypadku prawdopodobnie nie powinien zawracać sobie głowy żadnym z nich, który zawiera =
albo ;-)
./
stanowiło to problem, ale może być niepożądane w pewnych warunkach, takich jak przypadki, w których nazwa pliku musi zostać dołączona do wyjścia, w którym to przypadku ./
powinna być nadmiarowa i niepotrzebna, więc Muszę się go jakoś pozbyć. Oto co najmniej jeden przykład . Co do użytkownika, który wie, jakie są nazwy plików - cóż, w tym przypadku wiemy również, co to jest nazwa pliku, ale =
wciąż przeszkadza w prawidłowym przetwarzaniu. Więc prowadzenie może -
przeszkodzić.
./
przedrostka, aby obejść tę awk
(błędną) funkcję, ale potem uzyskasz wynik ./
wyjściowy, który możesz chcieć usunąć. Zobacz, jak sprawdzić, czy pierwszy wiersz pliku zawiera określony ciąg? jako przykład.
./
ale także globalny (ścieżka bezwzględna), /
dzięki czemu awk interpretuje argument jako plik.
W większości wersji awk argumentami po uruchomieniu programu są:
x=y
Ponieważ twoja nazwa pliku jest interpretowana jako przypadek nr 2, awk wciąż czeka na coś do odczytania na stdin (ponieważ nie rozpoznaje, że nazwa pliku została przekazana).
Przenośne zachowanie to jest udokumentowane w POSIX :
Można zmieszać jeden z dwóch poniższych typów argumentów:
- plik: ścieżka do pliku zawierającego dane wejściowe do odczytu, która jest dopasowywana do zestawu wzorców w programie. Jeżeli nie podano żadnych argumentów pliku lub jeśli argumentem pliku jest „-”, należy użyć standardowego wejścia.
- przypisanie: operand rozpoczynający się znakiem podkreślenia lub alfabetem z przenośnego zestawu znaków (patrz tabela w tomie Definicje podstawowe IEEE Std 1003.1-2001, sekcja 6.1, Przenośny zestaw znaków), po którym następuje sekwencja podkreśleń, cyfr, natomiast alfabet z przenośnego zestawu znaków, po którym następuje znak „=”, określa raczej przypisanie zmiennej niż nazwę ścieżki.
Jako taki, przenośnie, masz kilka opcji (# 1 jest prawdopodobnie najmniej inwazyjny):
awk ... ./my=file
, która omija to, ponieważ .
nie jest „znakiem podkreślenia lub alfabetu z przenośnego zestawu znaków”.awk ... < my=file
. Nie działa to jednak dobrze z wieloma plikami.ln my=file my_file
, a następnie użyć my_file
jak zwykle. Kopiowanie nie zostanie wykonane, a oba pliki zostaną poparte tymi samymi danymi i metadanymi i-węzłów. Po użyciu można bezpiecznie usunąć utworzone łącze, ponieważ liczba odwołań do i-węzła nadal będzie większa niż 0../my=file
działa? % awk 'processing_script_here' ./my=file.txt awk: fatal: cannot open file ./my=file.txt' for reading (No such file or directory).
Powinno być przenośne, ponieważ ./my
nie jest prawidłową nazwą zmiennej, więc nie powinno być analizowane w ten sposób.
=
jest poprzedzony znakiem podkreślenia lub alfabetem z przenośnego zestawu znaków (patrz tabela w tomie Definicje podstawowe IEEE Std 1003.1-2001, sekcja 6.1, Przenośny zestaw znaków), po którym następuje sekwencja znaków podkreślenia, cyfr i alfabetu z przenośnego zestawu znaków . więc ścieżka do pliku, taka jak ++foo=bar.txt
lub =foo
lub, ./foo=bar
wszystko jest w porządku jako taka .
lub +
nie jest [_a-zA-Z]
.
./my=file
zostaną przekazane dosłownie.
awk '{print $1,$2}' /etc/passwd
. Chodzi o to, że otwarcie pliku przez powłokę w przeciwieństwie do awk nie ma znaczenia, czy sprawia, że jest on widoczny, czy nie. W rzeczywistości awk '{exit}' < /etc/passwd
można oczekiwać awk
od końca pierwszego rekordu, exit
aby upewnić się, że pozostawi on pozycję w obrębie standardowego wejścia. POSIX tego wymaga. /usr/xpg4/bin/awk
robi to na Solarisie, ale ani gawk
nie mawk
wydaje się tego robić na GNU / Linux.
awk
ten sposób.
Aby zacytować dokumentację gawk (uwaga dodana):
Wszelkie dodatkowe argumenty w wierszu poleceń są zwykle traktowane jako pliki wejściowe do przetworzenia w określonej kolejności. Jednak argument, który ma postać var = wartość, przypisuje wartość do zmiennej var - w ogóle nie określa pliku.
Dlaczego polecenie zatrzymuje się i czeka? Ponieważ w formularzu awk 'processing_script_here' my=file.txt
nie ma pliku określonego przez powyższą definicję - my=file.txt
jest interpretowany jako przypisanie zmiennej, a jeśli nie ma zdefiniowanego pliku awk
, odczyta stdin (oczywiste z strace
którego wynika, że awk w takiej komendzie czeka na read(0,'...)
syscall.
Jest to również udokumentowane w specyfikacji awk POSIX , patrz sekcja OPERANDS i część przypisań )
Przypisanie zmiennych jest widoczne, awk '{print foo}' foo=bar /etc/passwd
ponieważ wartość foo
jest drukowana dla każdej linii w / etc / passwd. Podanie ./foo=bar
lub pełna ścieżka jednak działa.
Zauważ, że działa strace
on awk '1' foo=bar
jak sprawdzanie z cat foo=bar
pokazuje, że jest to specyficzny problem awk i execve nie pokazuje nazwę pliku jako argument przekazany, więc pociski nie mają nic wspólnego z przypisań zmiennych env w tej sprawie.
Dodatkowo należy pamiętać, że awk '...script...' foo=bar
nie spowoduje to tworzenia zmiennych środowiskowych przez powłokę, ponieważ przypisania zmiennych środowiskowych powinny poprzedzać polecenie, aby zadziałały. Patrz Zasady gramatyki powłoki POSIX , punkt nr 7. Dodatkowo można to sprawdzić za pomocąawk '{print ENVIRON["foo"]}' foo=bar /etc/passwd