Perché questo script funziona nel terminale ma non da un file?


19

Ho questo script di shell salvato in un file: fa un po 'di sostituzione di stringa di base.

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

Se lo incollo nella riga di comando, funziona benissimo:

$ 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"

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

Questo è l'output dell'eco sopra - funziona come previsto.

Ma, quando chiamo la sceneggiatura, con

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

Ottengo questo risultato:

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

Il mio script utilizza una versione diversa di bash o qualcosa del genere? Devo cambiare la mia linea shebang?


6
L'espansione di quel parametro è un basismo (beh, non POSIX): cambia il tuo shebang.
Jasonwryan,

Grazie! Ha funzionato Penso di essere un po 'confuso sulla differenza tra she bash. Lo leggerò. Se riesci a preoccuparti di trasformare il tuo commento in una risposta, lo segnerò correttamente.
Max Williams,

Ho dato il segno di spunta a The Duke per la sua risposta dettagliata, ma grazie comunque.
Max Williams,

2
@MaxWilliams: in poche parole, sh è la shell bourne originale. Tutti gli script compatibili dovrebbero essere scritti per sh. Ma molte shell successive (es: bash, Bourne Again shell) sono "quasi compatibili" e aggiungono molta più gentilezza. Come la sostituzione che hai usato. Al giorno d'oggi, sei abbastanza sicuro usando solo le funzionalità posix, ma tieni presente che la portabilità dipende anche da un set più ristretto (cioè solo sh). Quindi, in generale, usa: #!/usr/bin/env bashcome il tuo shebang e usa sostituzioni definite positivamente come desideri, ma con avvertenze sulla portabilità. E leggi: unix.stackexchange.com/a/48787/27616
Olivier Dulac il

Risposte:


37

Che cos'è sh

sh(o Shell Command Language) è un linguaggio di programmazione descritto dallo standard POSIX . Ha molte implementazioni ( ksh88, dash, ...). bashpuò anche essere considerata un'implementazione di sh(vedi sotto).

Perché shè una specifica, non un'implementazione, /bin/shè un collegamento simbolico (o un collegamento reale) a un'implementazione effettiva sulla maggior parte dei sistemi POSIX.

Cosa è bash

bashiniziato come shun'implementazione compatibile (anche se precede di alcuni anni lo standard POSIX), ma col passare del tempo ha acquisito molte estensioni. Molte di queste estensioni possono modificare il comportamento degli script shell POSIX validi, quindi di per sé bashnon è una shell POSIX valida. Piuttosto, è un dialetto del linguaggio shell POSIX.

bashsupporta uno --posixswitch, che lo rende più conforme a POSIX. Prova anche a imitare POSIX se invocato come sh.

sh = bash?

Per molto tempo, /bin/shusato per indicare /bin/bashsulla maggior parte dei sistemi GNU / Linux. Di conseguenza, era quasi diventato sicuro ignorare la differenza tra i due. Ma questo ha iniziato a cambiare di recente.

Alcuni esempi popolari di sistemi in cui /bin/shnon punta /bin/bash(e su alcuni dei quali /bin/bashpotrebbe anche non esistere) sono:

  1. Moderni sistemi Debian e Ubuntu, che si collegano shper dashimpostazione predefinita a symlink ;
  2. Busybox , che di solito viene eseguito durante l'avvio del sistema Linux come parte di initramfs. Utilizza l' ashimplementazione della shell.
  3. BSD, e in generale qualsiasi sistema non Linux. OpenBSD usa pdksh, un discendente della shell Korn. FreeBSD shè un discendente della shell Bourne UNIX originale. Solaris ha il suo shche per lungo tempo non era conforme a POSIX; un'implementazione gratuita è disponibile dal progetto Heirloom .

Come puoi scoprire cosa /bin/shindica il tuo sistema?

La complicazione è che /bin/shpotrebbe essere un collegamento simbolico o un collegamento reale. Se si tratta di un collegamento simbolico, un modo portatile per risolverlo è:

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

Se è un collegamento reale, prova

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

In effetti, il -Lflag copre sia i collegamenti simbolici che i collegamenti fisici, ma lo svantaggio di questo metodo è che non è portatile - POSIX non richiede find di supportare l' -samefileopzione, sebbene sia GNU find sia FreeBSD find lo supportino.

Linea Shebang

Alla fine, spetta a te decidere quale usare, scrivendo la riga «shebang».

Per esempio

#!/bin/sh

utilizzerà sh(e qualunque cosa accada),

#!/bin/bash

userà /bin/bashse è disponibile (e fallirà con un messaggio di errore se non lo è). Naturalmente, puoi anche specificare un'altra implementazione, ad es

#!/bin/dash

Quale usare

Per i miei script, preferisco shper i seguenti motivi:

  • è standardizzato
  • è molto più semplice e facile da imparare
  • è portatile su tutti i sistemi POSIX - anche se non lo sono bash, devono farlosh

Ci sono anche vantaggi nell'utilizzo bash. Le sue caratteristiche rendono la programmazione più conveniente e simile alla programmazione in altri linguaggi di programmazione moderni. Questi includono cose come variabili e array locali con ambito. Plain shè un linguaggio di programmazione molto minimalista.


Grazie per la risposta dettagliata: uso Linux Mint che è basato su Debian ed /bin/shè davvero un link simbolico a /bin/dash.
Max Williams,

Se vuoi aderire a POSIX sh, allora è meglio escludere completamente lo shebang: se la prima riga di un file di comandi shell inizia con i caratteri "#!", I risultati sono pubs.opengroup.org/onlinepubs/009695399/
Daniel Jour,

4
@DanielJour Se un qualsiasi sistema simile a Unix abbandonasse il solito supporto per le trans, si romperebbe così tante cose che sarebbe praticamente inutilizzabile. Quindi è uno standard di fatto, anche se non è specificato in POSIX. L'unico vero problema di compatibilità è che il percorso degli interpreti può essere diverso.
Barmar,

23

Aggiungendo all'ottima risposta di @ Hunter.S.Thompson, vorrei sottolineare che la parte non portatile dello script è

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

È ${variable/search/replace}un'estensione GNU. Ma puoi facilmente evitarlo con POSIX puro:

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

Seguendo Hunter, questa è la soluzione migliore che cambiare lo shebang in #! /bin/bash


Grazie - Sono d'accordo che è la correzione "più pura", ma preferirei che bash caricasse bash, poiché bash è ciò che uso abitualmente nel terminale (per impostazione predefinita) ed è più facile avere gli script che fanno la stessa cosa del normale riga di comando.
Max Williams,

1
@MaxWilliams Naturalmente, nessun problema. Ciò è stato principalmente indotto per il database di domande e risposte.
Philippos,
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.