Il modo di usare `/ usr / bin / env sed -f` in shebang?


Risposte:


33

Non puoi, portabilmente, mettere più di un argomento su una #!riga . Ciò significa solo un percorso completo e un argomento (ad esempio #!/bin/sed -fo #!/usr/bin/sed -f) o #!/usr/bin/envnessun argomento per l'interprete.

Una soluzione alternativa per ottenere uno script portatile consiste nell'utilizzare #!/bin/shun involucro della shell, passando lo script sed come argomento della riga di comando. Si noti che questo non è sanzionato da POSIX (gli script multiistruzione devono essere scritti con un -eargomento separato per ciascuna istruzione per la portabilità), ma funziona con molte implementazioni.

#!/bin/sh
exec sed '
s/a/b/
' "$@"

Per una sceneggiatura lunga, può essere più comodo usare una eredità. Un vantaggio di una eredità è che non è necessario citare le virgolette singole all'interno, se presenti. Un grande svantaggio è che la sceneggiatura è alimentata da sed sul suo input standard, con due fastidiose conseguenze. Alcune versioni di sed richiedono -f /dev/stdininvece di -f -, il che è un problema per la portabilità. Peggio ancora, lo script non può fungere da filtro, perché l'input standard è lo script e non può essere i dati.

#!/bin/sh
exec sed -f - -- "$@" <<'EOF'
s/a/b/
EOF

Il rovescio della medaglia dell'ereditarietà può essere risolto da un uso utile di cat. Dal momento che questo pone di nuovo l'intero script sulla riga di comando, non è conforme a POSIX, ma in pratica in gran parte portabile.

#!/bin/sh
exec sed "$(cat <<'EOF')" -- "$@"
s/a/b/
EOF

Un'altra soluzione alternativa è scrivere una sceneggiatura che può essere analizzata sia da sh che da sed. È portatile, abbastanza efficiente, solo un po 'brutto.

#! /bin/sh
b ()
{
x
}
i\
f true; then exec sed -f "$0" "$@"; fi
: ()
# sed script starts here
s/a/b/

spiegazioni:

  • Sotto sh: definisce una funzione chiamata b; i contenuti non contano finché la funzione è sintatticamente ben formata (in particolare, non è possibile avere una funzione vuota). Quindi, se vero (cioè sempre), esegui sedsullo script.
  • Sotto sed: ramo ()all'etichetta, quindi alcuni input ben formati. Quindi un icomando, che non ha alcun effetto perché viene sempre ignorato. Finalmente l' ()etichetta seguita dalla parte utile della sceneggiatura.
  • Testato sotto GNU sed, BusyBox e OpenBSD. (Puoi cavartela con qualcosa di più semplice su GNU sed, ma OpenBSD sed è esigente riguardo alle parti che salta.)

1
"o #!/usr/bin/enve nessun argomento." non è espresso molto bene. Forse "o #!/usr/bin/env sede nessun argomento da sed."
cjm,

+1 per "solo un po 'brutto" e la sceneggiatura in doppia lingua :)
retracile

6

Esistono varie implementazioni incompatibili di shebang (#!) A seconda del sistema operativo. Alcuni stanno costruendo un elenco completo di argomenti, alcuni stanno preservando il percorso del comando e mettono tutti gli argomenti rimanenti come uno singolo, alcuni stanno ignorando tutti gli argomenti e passano solo il percorso del comando, e infine, alcuni stanno passando l'intera stringa come un singolo comando. Sembra che tu sia in quest'ultimo caso.


2

env sta cercando di trovare un file con il nome "sed -f". Puoi provare "#! / Usr / bin / sed -f" come linea shebang.


2

A partire da GNU coreutils v8.30 , puoi fare:

#!/usr/bin/env -S sed -f

Questa caratteristica è stata aggiunta in un recente (2018/04/20) impegnarsi per env.cla GNU coreutils pacchetto, che ha aggiunto l' -So --split-stringopzione.

Dalla envpagina man:

OPTIONS
-S/--split-string usage in scripts
    The  -S  option allows specifing multiple parameters in a script.
    Running a script named 1.pl containing the following first line:

            #!/usr/bin/env -S perl -w -T

    Will execute perl -w -T 1.pl .

    Without the '-S' parameter the script will likely fail with:

            /usr/bin/env: 'perl -w -T': No such file or directory

    See the full documentation for more details.

Altri esempi sono disponibili nel manuale GNU coreutils .

Se usi anche l' -vopzione per un output dettagliato, puoi vedere esattamente come envdivide la stringa di argomenti:

In my_sed_script.sed:

#!/usr/bin/env -vS sed -f
s/a/b/

Esecuzione:

$ ./my_sed_script.sed
split -S:  ‘sed -f’
 into:    ‘sed’
     &    ‘-f’
executing: sed
   arg[0]= ‘sed’
   arg[1]= ‘-f’
   arg[2]= ‘./my_sed_script.sed’

Nota: questo vale solo per i shebang che usano /usr/bin/env, come --split-stringè una caratteristica di GNU in envparticolare.


1

Questa risposta fornisce un percorso per una soluzione elegante: /programming//a/1655389/642372

  1. read un'ereditarietà in una variabile shell.
  2. Passa quella variabile come argomento posizionale a sed.

Campione:

#!/usr/bin/env bash

read -rd '' SED_SCRIPT <<EOD      
# Your sed script goes here.
s/a/b/
EOD

exec sed "$SED_SCRIPT" "$@"

1

Mi piace la soluzione di Walker, ma può essere migliorata (inserendola in una risposta separata perché i commenti non accettano testo preformattato). Questo potrebbe non funzionare su tutte le versioni di Linux o Bash, ma su Ubuntu 17.10 è possibile ridurre il seguente:

#!/usr/bin/env bash
read SED_SCRIPT <<EOD
s/a/b/
EOD
sed "$SED_SCRIPT" "$@"

Puoi rimuovere evale semplificare il readcomando, ma devi eliminare il commento all'interno dell'eredità.

Inoltre, per tutto ciò che avresti sempre voluto sapere su sed ma che avevi paura di chiedere, c'è un tutorial molto utile su http://www.grymoire.com/unix/sed.html . Ciò suggerisce una soluzione ancora migliore, a scapito di perdere /usr/bin/enve codificare il percorso per sed:

#!/bin/sed -f
s/a/b/
s/c/d/

Ciò ha l'ulteriore vantaggio di supportare più di una s///sostituzione.

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.