¿Por qué este script funciona en la terminal pero no desde un archivo?

19

Tengo este script de shell guardado en un archivo: realiza una sustitución básica de cadenas.

#!/bin/sh
html_file=$1
echo "html_file = $html_file"
substr=.pdf
pdf_file="${html_file/.html/.pdf}"
echo "pdf_file = $pdf_file"

Si lo pego en la línea de comando, funciona bien:

$ html_file="/home/max/for_pauld/test_no_base64.html"
  echo "html_file = $html_file"
  substr=.pdf
  pdf_file="${html_file/.html/.pdf}"
  echo "pdf_file = $pdf_file"

da

html_file = /home/max/for_pauld/test_no_base64.html
pdf_file = /home/max/for_pauld/test_no_base64.pdf

Esa es la salida del eco anterior: funciona según lo previsto.

Pero cuando llamo el guión, con

$ saucer "/home/max/for_pauld/test_no_base64.html"

Me sale esta salida:

html_file = /home/max/for_pauld/test_no_base64.html
/home/max/bin/saucer: 5: /home/max/bin/saucer: Bad substitution

¿Mi script usa una versión diferente de bash o algo así? ¿Necesito cambiar mi línea shebang?

Max Williams
fuente
66
Esa expansión de parámetros es un bashism (bueno, no POSIX): cambia tu shebang.
jasonwryan
¡Gracias! Eso funciono. Creo que estoy un poco confuso sobre la diferencia entre shy bash. Leeré sobre eso. Si puede molestarse en hacer su comentario en una respuesta, lo marcaré como correcto.
Max Williams
Dio la señal al Duque por su respuesta detallada, pero gracias de todos modos.
Max Williams
2
@MaxWilliams: en pocas palabras, sh es la cáscara de bourne original. Todos los scripts compatibles deben escribirse para sh. Pero muchos shells posteriores (por ejemplo, bash, Bourne Again shell) son "casi compatibles" y agregan mucha más bondad. Tal como la sustitución que usaste. Hoy en día, está bastante seguro usando solo las funciones posix, pero tenga en cuenta que la portabilidad depende incluso de un conjunto más estrecho (es decir, solo sh). Entonces, en general, use: #!/usr/bin/env bashcomo su shebang, y use sustituciones definidas posixly como desee, pero con advertencias de portabilidad. Y lea: unix.stackexchange.com/a/48787/27616
Olivier Dulac

Respuestas:

37

Que es sh

sh(o el lenguaje de comandos de Shell) es un lenguaje de programación descrito por el estándar POSIX . Tiene muchas implementaciones ( ksh88, dash, ...). bashTambién se puede considerar una implementación de sh(ver más abajo).

Debido a que shes una especificación, no una implementación, /bin/shes un enlace simbólico (o un enlace duro) a una implementación real en la mayoría de los sistemas POSIX.

¿Qué es bash?

bashcomenzó como una shimplementación compatible (aunque es anterior al estándar POSIX por algunos años), pero con el paso del tiempo ha adquirido muchas extensiones. Muchas de estas extensiones pueden cambiar el comportamiento de los scripts de shell POSIX válidos, por bashlo que no es un shell POSIX válido. Más bien, es un dialecto del lenguaje de shell POSIX.

bashadmite un --posixconmutador, lo que lo hace más compatible con POSIX. También intenta imitar POSIX si se invoca como sh.

sh = bash?

Durante mucho tiempo, /bin/shsolía apuntar a la /bin/bashmayoría de los sistemas GNU / Linux. Como resultado, casi se había vuelto seguro ignorar la diferencia entre los dos. Pero eso comenzó a cambiar recientemente.

Algunos ejemplos populares de sistemas a los /bin/shque no apunta /bin/bash(y en algunos de los cuales /bin/bashpueden no existir) son:

  1. Los sistemas Debian y Ubuntu modernas, que SYMLINK shde dashforma predeterminada;
  2. Busybox , que generalmente se ejecuta durante el tiempo de arranque del sistema Linux como parte de initramfs. Utiliza la ashimplementación de shell.
  3. BSD y, en general, cualquier sistema que no sea Linux. OpenBSD utiliza pdksh, un descendiente del shell Korn. FreeBSD shes un descendiente del shell original Bourne de UNIX. Solaris tiene la suya shque durante mucho tiempo no fue compatible con POSIX; Se encuentra disponible una implementación gratuita del proyecto Heirloom .

¿Cómo puede averiguar qué /bin/shpuntos de su sistema?

La complicación es que /bin/shpodría ser un enlace simbólico o un enlace duro. Si es un enlace simbólico, una forma portátil de resolverlo es:

% file -h /bin/sh
/bin/sh: symbolic link to bash

Si es un enlace duro, intente

% find -L /bin -samefile /bin/sh
/bin/sh
/bin/bash

De hecho, la -Lbandera cubre tanto enlaces simbólicos como enlaces duros, pero la desventaja de este método es que no es portátil: POSIX no requiere find admitir la -samefileopción, aunque tanto GNU find como FreeBSD encuentran que lo admiten.

Línea Shebang

En última instancia, depende de usted decidir cuál usar, escribiendo la línea «shebang».

P.ej

#!/bin/sh

usará sh(y lo que sea que apunte),

#!/bin/bash

usará /bin/bashsi está disponible (y fallará con un mensaje de error si no lo está). Por supuesto, también puede especificar otra implementación, por ejemplo

#!/bin/dash

Cual usar

Para mis propios guiones, prefiero shpor las siguientes razones:

  • esta estandarizado
  • es mucho más simple y fácil de aprender
  • es portátil a través de los sistemas POSIX, incluso si no tienen bash, deben tenersh

Hay ventajas de usar bashtambién. Sus características hacen que la programación sea más conveniente y similar a la programación en otros lenguajes de programación modernos. Estos incluyen cosas como variables locales localizadas y matrices. Plain shes un lenguaje de programación muy minimalista.

Hunter.S.Thompson
fuente
Gracias por la respuesta detallada: utilizo Linux Mint, que está basado en Debian y de /bin/shhecho es un enlace simbólico /bin/dash.
Max Williams
Si desea adherirse a POSIX sh, es mejor que omita completamente el shebang: si la primera línea de un archivo de comandos de shell comienza con los caracteres "#!", Los resultados son pubs.opengroup.org/onlinepubs/009695399/
Daniel Jour
44
@DanielJour Si algún sistema similar a Unix dejara de lado el soporte habitual para shebangs, rompería tantas cosas que sería prácticamente inutilizable. Por lo tanto, es un estándar de facto, incluso si no se especifica en POSIX. El único problema de compatibilidad real es que la ruta a los intérpretes puede diferir.
Barmar
23

Además de la excelente respuesta de @ Hunter.S.Thompson, me gustaría señalar que la parte no portátil del guión es

pdf_file="${html_file/.html/.pdf}"

El ${variable/search/replace}es una extensión de GNU. Pero puede evitarlo fácilmente con POSIX puro:

pdf_file="${html_file%.html}".pdf

Después de Hunter, esta es la mejor solución que cambiar el shebang a #! /bin/bash

Philippos
fuente
Gracias. Estoy de acuerdo en que es la solución "más pura", pero prefiero el bash cargado de shebang, ya que bash es lo que uso habitualmente en el terminal (por defecto) y es más fácil hacer que los scripts solo hagan lo mismo que el habitual. línea de comando.
Max Williams
1
@MaxWilliams Por supuesto, no hay problema. Esto se asignó principalmente a la base de datos de preguntas y respuestas.
Philippos