Come stampare il proprio nome di script in mawk?


13

In bash $0contiene il nome dello script, ma in awk se creo uno script chiamato myscript.awk con il seguente contenuto:

#!/usr/bin/awk -f
BEGIN{ print ARGV[0] }

ed eseguirlo, stamperà solo "awk". Inoltre, ARGV [i] con i> 0 viene utilizzato solo per argomenti di script nella riga di comando. Quindi, come farlo stampare il nome dello script, in questo caso "myscript.awk"?


Ho cambiato il titolo da awk a mawk perché tutte le soluzioni richiedono gawk e non funzionano con awk generale, e in particolare con mawk che è ampiamente usato (ad es. Default su Ubuntu)
cipper

Cosa ti fa pensare di mawkdefault su Ubuntu? Sulla mia 15.04 VM, l'impostazione predefinita awkè gawk. Mentre è installato mawk non è l'impostazione predefinita.
Terdon

1
È uno script pazzo se lo chiami awk -f myscript.awk. Tuttavia, questo non è correlato al problema in questione.
cipper

1
@EdMorton È uno awkscript perché inizia con #!/usr/bin/awk -f. Gli script della shell iniziano con #!/bin/sh(o qualcosa di simile).
Barmar,

1
Ho parlato con vari esperti di shell e ho cercato di ottenere una risposta definitiva sul fatto che si tratti di uno script shell o awk e sorprendentemente secondo POSIX l'interpretazione dei file che iniziano con #! non è definito e non ha un nome di tipo specifico. Mentre alcune persone si riferiscono ad esso come uno "script di interprete di hash bang" piuttosto che uno script di shell o awk, il consenso sembra essere che dovrebbe essere considerato uno script awk anche se il kernel (non shell) interpreta la prima riga perché awk ancora deve essere in grado di analizzare anche quella prima riga (come commento) e puoi eseguirla usando awk -f file.
Ed Morton,

Risposte:


5

Con GNU awk 4.1.3 in bash su cygwin:

$ cat tst.sh
#!/bin/awk -f
BEGIN { print "Executing:", ENVIRON["_"] }

$ ./tst.sh
Executing: ./tst.sh

Non so quanto sia portatile. Come sempre, però, non eseguirò uno script awk usando uno shebang in uno script shell poiché ti deruba solo di possibili funzionalità. Mantienilo semplice e basta invece fare questo:

$ cat tst2.sh
awk -v cmd="$0" '
BEGIN { print "Executing:", cmd }
' "$@"

$ ./tst2.sh
Executing: ./tst2.sh

Quest'ultimo funzionerà con qualsiasi awk moderno in qualsiasi shell su qualsiasi piattaforma.


Nota che il primo funziona solo in bash, zsh o ksh. Il successivo riguarda lo script shell, non lo script awk.
cuonglm,

2
Grazie! ENVIRON["_"]funziona perfettamente e non chiama alcun programma esterno. La seconda opzione awk -v ...dipende da come si esegue lo script; Non lo voglio.
cipper

1
Chiamare la tua sceneggiatura tst.shè fuorviante. È uno awkscript, non uno script di shell. BEGINnon è un comando shell valido.
Barmar,

1
Giusto, ma la domanda sulla portabilità non è "è ENVIRON [] portatile" è " ENVIRON["_"]produce il percorso dello script della shell chiamante quando stampato da ogni awk chiamato tramite una shebang da ogni shell"? Non chiamerei mai uno script awk da uno shebang a me personalmente non mi interessa la risposta, ma ho pensato di menzionarlo ... Oh vedo nei commenti sopra che @cuonglm ha risposto che è supportato solo in alcune shell .
Ed Morton,

1
Buon punto, @Ed. Verificato come non funzionante nel trattino (che restituisce il comando precedente (o la shell stessa) anziché quello corrente). ksh93 prefigura in modo interessante il PID negli asterischi, ad es *12345*/tmp/test.awk. ARGV[0]è sempre affidabile awkin trattino, bash, zsh e ksh93.
Adam Katz,

5

Non penso che ciò sia possibile secondo la gawk documentazione :

Infine, il valore di ARGV[0](vedere la sezione 7.5 Variabili integrate) varia in base al sistema operativo in uso. Alcuni sistemi mettono awklì, alcuni mettono il percorso completo di awk (come /bin/awk), e alcuni mettono il nome del tuo script ('consiglio'). Non fare affidamento sul valore di ARGV[0]fornire il nome dello script.

Su linuxpuoi provare a usare una specie di hack sporco e come sottolineato nei commenti di Stéphane Chazelas è possibile se l'implementazione dei awkbyte NUL supporta:

#!/usr/bin/awk -f

BEGIN { getline t < "/proc/self/cmdline"; split(t, a, "\0"); print a[3]; }

la tua sceneggiatura così com'è sembra non funzionare. Stampa solo "k" se chiamato con "awk -f script.awk" e stampa "s" se chiamato da "./script.awk"
cipper

@cipper: qui funziona gawke fallisce (come la tua descrizione) con mawk. Interessante!

Funziona per me in Linux, awk- 4.0.2. In freebsd con /proc/curpoc/cmdline, e il awkrisultato è come il tuo ma funziona con gawk.
Taliezin,

Su Ubuntu predefinito non funziona. Sarebbe bello trovare una soluzione portatile.
cipper

1
@taliezin: la risposta di cuonglm non è una soluzione poiché richiede di alimentare manualmente lo script con il suo nome. È come chiamare awk -vNAME="myscript.awk" ./myscript.awke quindi stampare NAME all'interno dello script. Non una soluzione
cipper

5

Non conosco alcun modo diretto per ottenere il nome del comando da awk. Puoi comunque trovarlo attraverso una sotto-shell.

allocco

Con GNU awk e il pscomando è possibile utilizzare l'ID processo PROCINFO["PID"]per recuperare il nome del comando come soluzione alternativa. Per esempio:

cmdname.awk

#!/usr/bin/gawk -f

BEGIN {
  ("ps -p " PROCINFO["pid"] " -o comm=") | getline CMDNAME
  print CMDNAME
}

mawk e nawk

È possibile utilizzare lo stesso approccio, ma derivare awkil PID dalla $PPIDspeciale variabile di shell (PID del genitore):

cmdname.awk

#!/usr/bin/mawk -f

BEGIN { 
  ("ps -p $PPID -o comm=") | getline CMDNAME
  print CMDNAME
}

analisi

Esegui lo script in questo modo:

./cmdname.awk

Uscita in entrambi i casi:

cmdname.awk

Ho ricevuto un errore: / bin / sh: 1: -o: non trovato
cipper

@cipper: funziona solo con GNU awk, ho aggiunto la riga mancante di shebang.
Thor,

Dal manuale gawk : Secondo POSIX, 'espressione | getline 'è ambiguo se expression contiene operatori non personalizzati diversi da' $ ', ad esempio' "echo" "date" | getline 'è ambiguo perché l'operatore di concatenazione non è tra parentesi. Dovresti scriverlo come '("echo" "date") | getline 'se vuoi che il tuo programma sia portabile su tutte le implementazioni di awk.
cipper

1
Se necessario gawk, è una gawksoluzione anziché una awksoluzione. Penso che @cipper dovrebbe aggiungere il suo desiderio "una soluzione portatile" alla domanda.

1
@Thor: la risposta di cuonglm non è una soluzione poiché richiede di alimentare manualmente lo script con il suo nome. È come chiamare awk -vNAME="myscript.awk" ./myscript.awke quindi stampare NAME all'interno dello script. Non una soluzione
cipper

4

Con POSIX awk:

#!/usr/bin/awk -f

BEGIN {
    print ENVIRON["AWKSCRIPT"]
}

Poi:

AWKSCRIPT=test.awk ./test.awk
test.awk

4
Inserisci manualmente il nome dello script in esso, questo non è un modo di auto-stampa
cipper

@cipper: Beh, questo è il modo più semplice e portatile che posso immaginare.
cuonglm,

2
È come chiamare awk -vNAME="myscript.awk" ./myscript.awke quindi stampare la variabile NAMEall'interno dello script. Non una soluzione
cipper

@cipper: Questo è l'unico modo, se menzioni mawk. E anche usare ENVIRONnon è lo stesso che usare -vNAME="myscript.awk", da quando mawkespanderà la sequenza di escape in NAME.
cuonglm,

4

Usando GNU awk

Controllare la guida per l'utente di GNU awk - 7.5.2 Variabili integrate che trasmettono informazioni su cui mi sono imbattuto:

PROCINFO #

Gli elementi di questo array forniscono accesso alle informazioni sul programma awk in esecuzione. Sono garantiti i seguenti elementi (elencati in ordine alfabetico):

Procinfo [ "PID"]

L'ID processo del processo corrente.

Ciò significa che puoi conoscere il PID del programma durante il runtime. Quindi, si tratta di utilizzare system()per cercare il processo con questo dato PID:

#!/usr/bin/gawk -f
BEGIN{ pid=PROCINFO["pid"]
       system("ps -ef | awk '$2==" pid " {print $NF}'")
}

Io uso ps -ef, che visualizza il PID sulla seconda colonna. Supponendo che l'esecuzione sia eseguita awk -f <script>e senza altri parametri, possiamo supporre che l'ultimo campo della riga contenga le informazioni che vogliamo.

Nel caso in cui avessimo alcuni parametri, dovremmo analizzare la linea in modo diverso -o meglio usare alcune delle opzioni psper stampare solo le colonne che ci interessano.

Test

$ awk -f a.awk 
a.awk
$ cp a.awk hello.awk
$ awk -f hello.awk 
hello.awk

Nota anche che un altro capitolo della guida per l'utente di GNU awk ci dice che ARGV non è la strada da percorrere:

1.1.4 Programmi awk eseguibili

Infine, il valore di ARGV [0] (consultare Variabili integrate) varia in base al sistema operativo in uso. Alcuni sistemi mettono 'awk' lì, altri mettono il percorso completo di awk (come / bin / awk), e alcuni mettono il nome del tuo script ('consiglio'). (dc) Non fare affidamento sul valore di ARGV [0] per fornire il nome dello script.


purtroppo PROCINFO è solo una caratteristica gawk, non un generale awk. Ad esempio, non è disponibile in mawk (che è installato di default in Ubuntu)
cipper

Lo so ... Perché hai taggato la domanda con [gawk] allora?
fedorqui,

Hai ragione. Quando ho pubblicato la domanda non ero a conoscenza di tutte queste differenze tra mawk e gawk. Il tag è ora cambiato in mawk.
cipper

@cipper good:) In effetti stavo testando mawke non riuscivo a farlo funzionare, quindi ho installato gawksul mio Ubuntu e ha funzionato. Quindi una soluzione alternativa può essere quella di gawk: D
fedorqui

1
@terdon, gawknon è installato di default su Ubuntu (o almeno su alcune versioni di Ubuntu, dove si mawktrova l' awkimplementazione predefinita ). IIRC, ho dovuto installarlo anche su Debian.
Stéphane Chazelas,
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.