awk 'processing_script_here' my=file.txt
sembra fermarsi e aspettare indefinitamente ...
Cosa sta succedendo qui e come faccio a farlo funzionare?
awk 'processing_script_here' my=file.txt
sembra fermarsi e aspettare indefinitamente ...
Cosa sta succedendo qui e come faccio a farlo funzionare?
Risposte:
Come dice Chris , gli argomenti del modulo variablename=anything
vengono trattati come assegnazione di variabili (che vengono eseguiti nel momento in cui gli argomenti vengono elaborati rispetto a quelli (più recenti) -v var=value
eseguiti prima delle BEGIN
istruzioni) anziché come nomi di file di input.
Questo può essere utile in cose come:
awk '{print $1}' FS=/ RS='\n' file1 FS='\n' RS= file2
Dove è possibile specificare un diverso FS
/ RS
per file. È anche comunemente usato in:
awk '!file1_processed{a[$0]; next}; {...}' file1 file1_processed=1 file2
Quale è una versione più sicura di:
awk 'NR==FNR{a[$0]; next}; {...}' file1 file2
(che non funziona se file1
è vuoto)
Ma questo si intromette quando hai file il cui nome contiene =
caratteri.
Ora, questo è solo un problema quando ciò che rimane del primo =
è un awk
nome di variabile valido .
Ciò che costituisce un nome di variabile valido in awk
è più rigoroso di in sh
.
POSIX richiede che sia simile a:
[_a-zA-Z][_a-zA-Z0-9]*
Con solo caratteri del set di caratteri portatile. Tuttavia, il /usr/xpg4/bin/awk
Solaris 11 almeno non è conforme a tale riguardo e consente qualsiasi carattere alfabetico nella locale in nomi di variabili, non solo a-zA-Z.
Quindi un argomento come x+y=foo
o =bar
o ./foo=bar
è ancora trattato come un nome di file di input e non come un'assegnazione poiché ciò che rimane del primo =
non è un nome di variabile valido. Un argomento come Stéphane=Chazelas.txt
può o no, a seconda awk
dell'implementazione e delle impostazioni internazionali.
Ecco perché con awk, si consiglia di utilizzare:
awk '...' ./*.txt
invece di
awk '...' *.txt
per esempio per evitare il problema se non puoi garantire che il nome dei txt
file non contenga =
caratteri.
Inoltre, -vfoo=bar.txt
fai attenzione che un argomento simile può essere trattato come un'opzione se usi:
awk -f file.awk -vfoo=bar.txt
(vale anche per awk '{code}' -vfoo=bar.txt
le awk
versioni from busybox precedenti alla 1.28.0, consultare la relativa segnalazione di bug ).
Ancora una volta, usare ./*.txt
aggira quello (usare un ./
prefisso aiuta anche con un file chiamato -
che altrimenti awk
capisce invece come input standard ).
Questo è anche il motivo
#! /usr/bin/awk -f
le shebang non funzionano davvero. Mentre var=value
quelli possono essere aggirati fissando i ARGV
valori (aggiungi un ./
prefisso) in BEGIN
un'istruzione:
#! /usr/bin/awk -f
BEGIN {
for (i = 1; i < ARGC; i++)
if (ARGV[i] ~ /^[_[:alpha:]][_[:alnum:]]*=/)
ARGV[i] = "./" ARGV[i]
}
# rest of awk script
Ciò non aiuta con le opzioni come quelle viste awk
e non dalla awk
sceneggiatura.
Un potenziale problema estetico con l'uso di quel ./
prefisso è che finisce FILENAME
, ma puoi sempre usarlo substr(FILENAME, 3)
per toglierlo se non lo vuoi.
L'implementazione GNU di awk
risolve tutti questi problemi con la sua -E
opzione.
Dopodiché -E
, gawk si aspetta solo il percorso dello awk
script (dove -
significa ancora stdin) e quindi un elenco di soli percorsi dei file di input (e lì non -
viene nemmeno trattato in modo speciale).
È appositamente progettato per:
#! /usr/bin/gawk -E
shebangs in cui l'elenco degli argomenti sono sempre file di input (tieni presente che sei ancora libero di modificare tale ARGV
elenco in BEGIN
un'istruzione).
Puoi anche usarlo come:
gawk -e '...awk code here...' -E /dev/null *.txt
Usiamo -E
con uno script vuoto ( /dev/null
) solo per assicurarci che quelli che *.txt
seguono siano sempre trattati come file di input, anche se contengono =
caratteri.
../foo
, /path/to/foo
e percorsi che hanno una codifica diversa) - nel qual caso substr(FILENAME,3)
non sarà sufficiente, oppure è uno script one-shot in cui l'utente conosce praticamente quali sono i nomi dei file - nel qual caso probabilmente non dovrebbe preoccuparsi di nessuno di essi contenente =
nessuno dei due ;-)
./
è un problema, ma che potrebbe essere indesiderabile in determinate condizioni, come casi in cui il nome file deve essere incluso nell'output, nel qual caso ./
dovrebbe essere ridondante e non necessario, quindi è necessario dovrò liberarmene in qualche modo. Ecco almeno un esempio . Per quanto riguarda l'utente che sa quali sono i nomi dei file - beh, in questo caso sappiamo anche cos'è il nome file, ma =
ostacola comunque l'elaborazione corretta. Quindi può -
mettersi in cammino.
./
prefisso per aggirare quella awk
(mis) funzionalità ma poi finisci con un ./
output in output che potresti voler eliminare. Vedi come verificare se la prima riga del file contiene una stringa specifica? come esempio.
./
ma anche il globale (percorso assoluto) /
che rende awk interpretare l'argomento come un file.
Nella maggior parte delle versioni di awk, gli argomenti dopo l'esecuzione del programma sono:
x=y
Dato che il tuo nome file viene interpretato come caso n. 2, awk sta ancora aspettando qualcosa da leggere su stdin (poiché non percepisce che è stato passato alcun nome file).
Portabilmente, questo comportamento è documentato in POSIX :
È possibile mescolare uno dei due seguenti tipi di argomento:
- file: un percorso di un file che contiene l'input da leggere, che viene confrontato con l'insieme di schemi nel programma. Se non viene specificato alcun operando di file o se un operando di file è "-", deve essere utilizzato l'input standard.
- assegnazione: un operando che inizia con un carattere di sottolineatura o alfabetico dal set di caratteri portatile (vedere la tabella nel volume Definizioni di base di IEEE Std 1003.1-2001, Sezione 6.1, Set di caratteri portatile), seguito da una sequenza di caratteri di sottolineatura, cifre, e gli alfabeti del set di caratteri portatile, seguito dal carattere '=', devono specificare un'assegnazione variabile anziché un nome percorso.
Come tale, portabilmente, hai alcune opzioni (il numero 1 è probabilmente il meno invadente):
awk ... ./my=file
, che elude questo dato che .
non è "un carattere di sottolineatura o alfabetico dal set di caratteri portatile".awk ... < my=file
. Tuttavia, questo non funziona bene con più file.ln my=file my_file
, quindi usarlo my_file
normalmente. Non verrà eseguita alcuna copia ed entrambi i file saranno supportati dagli stessi dati e metadati inode. Dopo averlo usato, è sicuro rimuovere il collegamento creato poiché il numero di riferimenti all'inode sarà comunque maggiore di 0../my=file
funziona? % awk 'processing_script_here' ./my=file.txt awk: fatal: cannot open file ./my=file.txt' for reading (No such file or directory).
Questo dovrebbe essere portatile perché ./my
non è un nome di variabile valido, quindi non dovrebbe essere analizzato in questo modo.
=
è preceduto da un carattere di sottolineatura o alfabetico dal set di caratteri portatile (vedere la tabella nel volume Definizioni di base di IEEE Std 1003.1-2001, Sezione 6.1, Set di caratteri portatile), seguito da una sequenza di caratteri di sottolineatura, cifre e alfabeti dal set di caratteri portatile . quindi un percorso di file come ++foo=bar.txt
o =foo
o ./foo=bar
sono tutti OK come quello .
o +
non è un [_a-zA-Z]
.
./my=file
sarà passato alla lettera.
awk '{print $1,$2}' /etc/passwd
. Il punto è che avere la shell aperta il file invece di awk non fa alcuna differenza se lo rende ricercabile o meno. In realtà, awk '{exit}' < /etc/passwd
ti aspetteresti awk
di tornare alla fine del primo disco exit
per assicurarti che lasci la posizione all'interno dello stdin. POSIX lo richiede. /usr/xpg4/bin/awk
lo fa su Solaris, ma gawk
né mawk
sembra né farlo su GNU / Linux.
awk
questo modo.
Per citare la documentazione gawk (nota l'enfasi aggiunta):
Eventuali argomenti aggiuntivi sulla riga di comando vengono normalmente trattati come file di input da elaborare nell'ordine specificato. Tuttavia, un argomento che ha la forma var = value, assegna il valore value alla variabile var: non specifica affatto un file.
Perché il comando si ferma e aspetta? Perché nel modulo awk 'processing_script_here' my=file.txt
non c'è alcun file specificato dalla definizione sopra - my=file.txt
viene interpretato come assegnazione di variabili, e se non c'è alcun file definito awk
leggerà stdin (anche evidente dal strace
quale dimostra che awk in tale comando è in attesa su read(0,'...)
syscall.
Questo è anche documentato nelle specifiche awk di POSIX , vedere la sezione OPERANDS e parte degli incarichi )
L'assegnazione delle variabili è evidente in awk '{print foo}' foo=bar /etc/passwd
quanto il valore di foo
è stampato per ogni riga in / etc / passwd. La specifica ./foo=bar
o il percorso completo tuttavia funziona.
Si noti che in esecuzione strace
su awk '1' foo=bar
così come il controllo con cat foo=bar
spettacoli che si tratta di problema awk-specifici, e execve non mostra il nome del file come argomento passato, in modo da gusci non hanno nulla a che fare con le assegnazioni variabile ENV in questo caso.
Inoltre, si noti che awk '...script...' foo=bar
non causerà la creazione di variabili di ambiente tramite shell, poiché le assegnazioni di variabili di ambiente dovrebbero precedere un comando per avere effetto. Vedere le Regole di grammatura della shell POSIX , punto numero 7. Inoltre, questo può essere verificato tramiteawk '{print ENVIRON["foo"]}' foo=bar /etc/passwd