Come usare più argomenti per awk con uno shebang (cioè #!)?


118

Vorrei eseguire uno script gawk--re-interval usando uno shebang. L'approccio "ingenuo" di

#!/usr/bin/gawk --re-interval -f
... awk script goes here

non funziona, poiché gawk viene chiamato con il primo argomento "--re-interval -f"(non diviso attorno agli spazi), che non comprende. C'è una soluzione alternativa per questo?

Ovviamente non puoi chiamare gawk direttamente ma racchiuderlo in uno script di shell che divide il primo argomento, o creare uno script di shell che poi chiami gawk e metta lo script in un altro file, ma mi chiedevo se ci fosse un modo per farlo questo all'interno di un file.

Il comportamento delle linee shebang differisce da sistema a sistema - almeno in Cygwin non divide gli argomenti con spazi bianchi. Mi interessa solo come farlo su un sistema che si comporta in questo modo; lo script non è pensato per essere portabile.


1
Uno sciocco esperimento che ho appena fatto è stato con uno script utilizzando un altro script sulla riga shebang, che ha diviso correttamente gli argomenti.
Hasturkun

@Hasturkun, questo solleva un altro problema, che anche il comportamento delle linee shebang differisce da sistema a sistema rispetto al fatto che il programma invocato possa essere esso stesso uno script.
dubiousjim


Con le versioni recenti di gawk (> = 4.0), --re-intervalnon è più necessario (vedere [ gnu.org/software/gawk/manual/… ).

Risposte:


25

Questo sembra funzionare per me con (g) awk.

#!/bin/sh
arbitrary_long_name==0 "exec" "/usr/bin/gawk" "--re-interval" "-f" "$0" "$@"


# The real awk program starts here
{ print $0 }

Notare la #! esecuzioni /bin/sh, quindi questo script viene prima interpretato come uno script di shell.

All'inizio, ho semplicemente provato "exec" "/usr/bin/gawk" "--re-interval" "-f" "$0" "$@" , ma awk lo ha trattato come un comando e ha stampato incondizionatamente ogni riga di input. Questo è il motivo per cui ho inserito il arbitrary_long_name==0- dovrebbe fallire tutto il tempo. Potresti sostituirlo con una stringa senza senso. Fondamentalmente, stavo cercando una condizione falsa in awk che non avrebbe influenzato negativamente lo script della shell.

Nello script della shell, arbitrary_long_name==0definisce una variabile chiamata arbitrary_long_namee la imposta uguale a =0.


Questa è la mia risposta, ma mi chiedo se sia sufficientemente portatile e robusto. Dipende specificamente da bash, o funzionerà con qualsiasi POSIX sh? E non lo uso awkspesso, quindi non sono sicuro che il mio trucco sulla seconda riga sia un buon modo per forzare awka ignorare la riga.
Aaron McDaid

Proprio quello che mi chiedevo, +1, ma probabilmente sconsigliabile (da qui i voti relativi).
Aaron Hall

Puoi spiegare quali problemi potrebbe avere, @AaronHall? Finché la variabile arbitrary_long_namenon si scontra con una variabile utilizzata nel programma awk reale, non vedo alcun problema. C'è qualcosa che mi manca?
Aaron McDaid

Utilizzare #!/bin/sh -invece di #!/bin/shper proteggere lo script da possibili comportamenti anomali in modo pericoloso se invocato con un argomento zero che ha -come primo carattere. Ciò può accadere accidentalmente in linguaggi di programmazione come C, dove è facile rovinare accidentalmente dimenticando di passare il nome del programma richiamato come parte dell'array di argomenti a execvefunzioni simili e se le persone abitualmente dimenticano di proteggersi, può anche finiscono per essere l'ultimo passaggio di una vulnerabilità sfruttabile in modo dannoso che consente a un utente malintenzionato di ottenere una shell interattiva.
mtraceur

161

La linea Shebang non è mai stata specificata come parte di POSIX, SUS, LSB o qualsiasi altra specifica. AFAIK, non è stato nemmeno adeguatamente documentato.

C'è un consenso approssimativo su ciò che fa: prendere tutto tra il !e il \ne exec. Il presupposto è che tutto ciò che si trova tra il !e il \nè un percorso completo e assoluto per l'interprete. Non c'è consenso su cosa succede se contiene spazi bianchi.

  1. Alcuni sistemi operativi trattano semplicemente l'intera cosa come il percorso. Dopo tutto, nella maggior parte dei sistemi operativi, spazi bianchi o trattini sono legali in un percorso.
  2. Alcuni sistemi operativi si dividono in spazi bianchi e trattano la prima parte come il percorso dell'interprete e il resto come argomenti individuali.
  3. Alcuni sistemi operativi si dividono al primo spazio bianco e trattano la parte anteriore come il percorso dell'interpeter e il resto come un singolo argomento (che è ciò che stai vedendo).
  4. Alcuni addirittura non supportano linee shebang a tutti .

Per fortuna, 1. e 4. sembrano essersi estinti, ma 3. è abbastanza diffuso, quindi semplicemente non puoi fare affidamento sulla possibilità di superare più di un argomento.

E dal momento che la posizione dei comandi è, inoltre, non specificato in POSIX o SUS, in genere si usa up che singolo argomento passando dell'eseguibile nome di envmodo che esso possa determinare la posizione del file eseguibile; per esempio:

#!/usr/bin/env gawk

[Ovviamente, questo presuppone ancora un percorso particolare env, ma ci sono solo pochissimi sistemi in cui vive /bin, quindi questo è generalmente sicuro. La posizione di envè molto più standardizzata rispetto alla posizione di gawko, peggio ancora, qualcosa come pythono rubyo spidermonkey.]

Il che significa che non si può effettivamente utilizzare eventuali argomenti affatto .


1
L'env di FreeBSD ha un -Sinterruttore che aiuta qui, ma non è presente sul mio Linux env, e sospetto che non sia disponibile nemmeno su gygwin. @hstoerr, altri utenti con situazioni diverse potrebbero leggere le tue domande in un secondo momento, quindi in generale le risposte portatili sono preferibili, anche se ora non hai bisogno della portabilità.
dubiousjim

4
Quindi non possiamo usare in modo portabile gli argomenti in una faccenda. Ma cosa succede se abbiamo bisogno di argomenti con qualsiasi mezzo necessario? Immagino che la soluzione sia scrivere uno script di shell wrapper contenente #!/bin/she /usr/bin/env gawk --re-interval -f my-script.awk. È corretto?
Rory O'Kane

1
Non sono d'accordo. Puoi usare abbastanza portabilmente un argomento. Qualsiasi sistema in cui non è possibile utilizzare alcun argomento fallisce miseramente nell'implementare questo Unixismo tradizionale, che è ciò che è hash-bang. Se le non implementazioni sono un gioco leale, possiamo tranquillamente affermare che di per #!sé non è portabile. Ad esempio, Windows non riconosce affatto questa convenzione "nativamente". Tradizionalmente un argomento ha il botto su Unix per poterlo fare #!/usr/bin/awk -f.
Kaz

7
@Kaz: Sì, ma poiché i percorsi di molti binari non sono standardizzati, usi il tuo unico argomento per #!/usr/bin/env rubyi Mi piace.
Jörg W Mittag

3
@Pacerier: modifica la specifica POSIX e attendi 20-30 anni fino a quando tutti i sistemi non sono stati aggiornati per essere conformi alle specifiche.
Jörg W Mittag

18

Sebbene non sia esattamente portabile, a partire da coreutils 8.30 e secondo la sua documentazione sarai in grado di utilizzare:

#!/usr/bin/env -S command arg1 arg2 ...

Così dato:

$ cat test.sh
#!/usr/bin/env -S showargs here 'is another' long arg -e "this and that " too

otterrete:

% ./test.sh 
$0 is '/usr/local/bin/showargs'
$1 is 'here'
$2 is 'is another'
$3 is 'long'
$4 is 'arg'
$5 is '-e'
$6 is 'this and that '
$7 is 'too'
$8 is './test.sh'

e nel caso foste curiosi showargsè:

#!/usr/bin/env sh
echo "\$0 is '$0'"

i=1
for arg in "$@"; do
    echo "\$$i is '$arg'"
    i=$((i+1))
done

Risposta originale qui .


1
Cordiali saluti, FreeBSD ha avuto -S per anni (dalla 6.0). Questa è una gradita aggiunta alla portabilità di coreutils.
Juan

12

Mi sono imbattuto nello stesso problema, senza una soluzione apparente a causa del modo in cui gli spazi bianchi vengono trattati in un lampo (almeno su Linux).

Tuttavia, puoi passare diverse opzioni in uno shebang, purché siano opzioni brevi e possano essere concatenate (nel modo GNU).

Ad esempio, non puoi avere

#!/usr/bin/foo -i -f

ma puoi avere

#!/usr/bin/foo -if

Ovviamente, funziona solo quando le opzioni hanno brevi equivalenti e non accettano argomenti.


11

Sotto Cygwin e Linux tutto ciò che segue il percorso dello shebang viene analizzato nel programma come un argomento.

È possibile aggirare questo problema usando un altro awkscript all'interno dello shebang:

#!/usr/bin/gawk {system("/usr/bin/gawk --re-interval -f " FILENAME); exit}

Questo verrà eseguito {system("/usr/bin/gawk --re-interval -f " FILENAME); exit}in awk.
E questo verrà eseguito /usr/bin/gawk --re-interval -f path/to/your/script.awknella shell del tuo sistema.


2
questo non funzionerà se hai passato argomenti alla sceneggiatura
Steven Penny

4
#!/bin/sh
''':'
exec YourProg -some_options "$0" "$@"
'''

Il trucco di shell shebang sopra è più portatile di /usr/bin/env.


Il '' ':' è un hold-over perché la mia soluzione originale era per uno script python così il '' ':' dice all'interprete python di ignorare la parte exec.
user3123730

4
Penso che tu sia stato sottovalutato perché la tua soluzione è per python, ma questa domanda riguarda awk.
Aaron McDaid

1
Ottimo trucco per Python.
Zaar Hai

3

Nel manuale di gawk (http://www.gnu.org/manual/gawk/gawk.html), alla fine della sezione 1.14 si nota che si dovrebbe usare un solo argomento quando si esegue gawk da una riga di shebang. Dice che il sistema operativo tratterà tutto dopo il percorso di gawk come un singolo argomento. Forse c'è un altro modo per specificare l' --re-intervalopzione? Forse il tuo script può fare riferimento alla tua shell nella riga shebang, essere eseguito gawkcome un comando e includere il testo del tuo script come "here document".


Sembra che non ci sia altro modo per specificare l'opzione. Hai ragione: gawk -f - << EOF, alcune righe di script, EOF funziona, ma mi preclude la lettura dello standard input con gawk.
Hans-Peter Störr

Il documento here consuma il flusso di input standard per gawk, ma potresti comunque essere in grado di reindirizzare qualcosa su stderr (ovvero, reindirizzare stdout a stderr prima di collegarsi a questo script). Non l'ho mai provato, ma fino a quando il primo processo non emette nulla su stderr, potrebbe funzionare. Puoi anche creare una named pipe ( linuxjournal.com/content/using-named-pipes-fifos-bash ) se vuoi assicurarti che nient'altro la stia usando.
bta

3

Perché non usare bashe gawkse stesso, per saltare oltre Shebang, leggere lo script e passarlo come file a una seconda istanza di gawk [--with-whatever-number-of-params-you-need]?

#!/bin/bash
gawk --re-interval -f <(gawk 'NR>3' $0 )
exit
{
  print "Program body goes here"
  print $1
}

(-Lo stesso potrebbe naturalmente essere ottenuto anche con ad esempio sedo tail, ma penso che ci sia un qualche tipo di bellezza che dipende solo da bashe da gawkse stesso;)


0

Solo per divertimento: c'è la seguente soluzione piuttosto strana che reindirizza stdin e il programma attraverso i descrittori di file 3 e 4. Puoi anche creare un file temporaneo per lo script.

#!/bin/bash
exec 3>&0
exec <<-EOF 4>&0
BEGIN {print "HALLO"}
{print \$1}
EOF
gawk --re-interval -f <(cat 0>&4) 0>&3

Una cosa è fastidiosa in questo: la shell esegue l'espansione delle variabili sullo script, quindi devi citare ogni $ (come fatto nella seconda riga dello script) e probabilmente di più.


-1

Per una soluzione portatile, usa awkinvece di gawk, invoca la shell BOURNE standard ( /bin/sh) con il tuo shebang e invoca awkdirettamente, passando il programma sulla riga di comando come un here document piuttosto che tramite stdin:

#!/bin/sh
gawk --re-interval <<<EOF
PROGRAM HERE
EOF

Nota: nessun -fargomento per awk. Ciò lascia a stdindisposizione per awkleggere l'input da. Supponendo che tu abbia gawkinstallato e sul tuo PATH, questo ottiene tutto ciò che penso stavi cercando di fare con il tuo esempio originale (supponendo che volessi che il contenuto del file fosse lo script awk e non l'input, che penso che il tuo approccio shebang lo avrebbe trattato come ).


3
Non ha funzionato per me. L'uomo bash dice <<< blabla mette blabla su stdin. Volevi dire << - EOF? Ad ogni modo, questo mette anche il programma su stdin.
Hans-Peter Störr
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.