La façon d'utiliser `/ usr / bin / env sed -f` dans shebang?


Réponses:


33

Vous ne pouvez pas, de manière portative, mettre plus d'un argument sur une #!ligne . Cela signifie seulement un chemin complet et un argument (par exemple #!/bin/sed -fou #!/usr/bin/sed -f), ou #!/usr/bin/envet aucun argument à l'interpréteur.

Une solution de contournement pour obtenir un script portable consiste à utiliser #!/bin/shun wrapper shell, en passant le script sed comme argument de ligne de commande. Notez que ceci n'est pas autorisé par POSIX (les scripts multi-instructions doivent être écrits avec un -eargument séparé pour chaque instruction pour la portabilité), mais cela fonctionne avec de nombreuses implémentations.

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

Pour un long script, il peut être plus pratique d'utiliser un hérédoc. Un avantage d'un hérédoc est que vous n'avez pas besoin de citer les guillemets simples à l'intérieur, le cas échéant. Un inconvénient majeur est que le script est alimenté par sed sur son entrée standard, avec deux conséquences ennuyeuses. Certaines versions de sed nécessitent -f /dev/stdinau lieu de -f -, ce qui pose un problème de portabilité. Pire, le script ne peut pas agir comme un filtre, car l'entrée standard est le script et ne peut pas être les données.

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

L'inconvénient de l'hérédoc peut être corrigé par une utilisation utile de cat. Dans la mesure où cela place à nouveau l'intégralité du script sur la ligne de commande, il n'est pas compatible POSIX, mais largement portable en pratique.

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

Une autre solution consiste à écrire un script qui peut être analysé à la fois par sh et par sed. C'est portable, assez efficace, juste un peu moche.

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

Explications:

  • Sous sh: définir une fonction appelée b; le contenu n'a pas d'importance tant que la fonction est syntaxiquement bien formée (en particulier, vous ne pouvez pas avoir de fonction vide). Ensuite, si vrai (c'est-à-dire toujours), exécutez sedle script.
  • Sous sed: branchez-vous sur l' ()étiquette, puis une entrée bien formée. Ensuite, une icommande, qui n'a aucun effet car elle est toujours ignorée. Enfin l' ()étiquette suivie de la partie utile du script.
  • Testé sous GNU sed, BusyBox et OpenBSD. (Vous pouvez vous en sortir avec quelque chose de plus simple sur GNU sed, mais OpenBSD sed est pointilleux sur les parties qu'il ignore.)

1
"ou #!/usr/bin/envet aucun argument." n'est pas très bien formulé. Peut-être "ou #!/usr/bin/env sedet aucun argument pour sed."
cjm

+1 pour "juste un peu moche" et le script
bilingue

6

Il existe différentes implémentations incompatibles du shebang (#!) Selon le système d'exploitation. Certains construisent une liste complète d'arguments, certains préservent le chemin de commande et mettent tous les arguments restants en un seul, certains ignorent tous les arguments et ne transmettent que le chemin de commande, et enfin, certains passent la chaîne entière comme un seul commander. Vous semblez être dans ce dernier cas.


2

env essaie de trouver un fichier avec le nom "sed -f". Vous pouvez essayer "#! / Usr / bin / sed -f" comme ligne de shebang.


2

Depuis GNU coreutils v8.30 , vous pouvez faire:

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

Cette fonctionnalité a été ajoutée dans une récente (20/04/2018) engage à env.cdans le paquet de GNU, qui a ajouté la -Sou --split-stringoption.

Depuis la envpage de manuel:

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.

D'autres exemples sont disponibles dans le manuel GNU coreutils .

Si vous utilisez également l' -voption pour une sortie détaillée, vous pouvez voir exactement comment envfractionne la chaîne d'arguments:

Dans my_sed_script.sed:

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

Exécution:

$ ./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’

Remarque: Cela ne s'applique qu'aux shebangs qui utilisent /usr/bin/env, comme --split-stringc'est une caractéristique de GNU en envparticulier.


1

Cette réponse fournit un chemin vers une solution élégante: /programming//a/1655389/642372

  1. read un hérédoc dans une variable shell.
  2. Passez cette variable comme argument positionnel à sed.

Échantillon:

#!/usr/bin/env bash

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

exec sed "$SED_SCRIPT" "$@"

1

J'aime la solution de Walker, mais elle peut être améliorée (en mettant cela dans une réponse séparée car les commentaires n'acceptent pas le texte préformaté). Cela peut ne pas fonctionner sur toutes les versions de Linux ou de Bash, mais sur Ubuntu 17.10, vous pouvez le réduire comme suit:

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

Vous pouvez supprimer evalet simplifier la readcommande, mais vous devez vous débarrasser du commentaire dans l'hérédoc.

De plus, pour tout ce que vous avez toujours voulu savoir sur sed mais que vous aviez peur de demander, il y a un tutoriel très utile sur http://www.grymoire.com/unix/sed.html . Cela suggère une solution encore meilleure, au détriment de la perte /usr/bin/envet du codage en dur du chemin vers sed:

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

Cela présente l'avantage supplémentaire de prendre en charge plusieurs s///remplacements.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.