Почему этот скрипт работает в терминале, а не из файла?

19

У меня есть этот сценарий оболочки, сохраненный в файле: он выполняет некоторые основные подстановки строк.

#!/bin/sh
html_file=$1
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"
  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

Это вывод из эха выше - он работает как задумано.

Но когда я вызываю сценарий, с

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

Я получаю этот вывод:

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

Мой сценарий использует другую версию Bash или что-то? Мне нужно изменить мою линию Шебанга?

Макс Уильямс
источник
6
Расширение этого параметра является башизмом (ну, не POSIX): измените свой шебанг.
Джейсонвриан
Благодарность! Это сработало. Я думаю, что я немного смутный на разнице между shи bash. Я прочитаю об этом. Если вы не можете превратить ваш комментарий в ответ, я отмечу его как правильный.
Макс Уильямс
Дали галочку герцогу за его подробный ответ, но все равно спасибо.
Макс Уильямс
2
@MaxWilliams: в двух словах, sh - это оригинальная оболочка Борна. Все совместимые скрипты должны быть написаны для sh. Но многие последующие оболочки (например, bash, оболочка Bourne Again) «почти совместимы» и добавляют много дополнительной привлекательности. Такие как замена, которую вы использовали. В настоящее время вы совершенно безопасны, используя только функции posix, но имейте в виду, что переносимость зависит даже от более узкого набора (т. Е. Только от sh). Так что, в общем, используйте: #!/usr/bin/env bashкак ваш shebang, и используйте заданные по порядку замены, как вы хотите, но с оговорками переносимости. И читайте: unix.stackexchange.com/a/48787/27616
Оливье Дюлак

Ответы:

37

Что такое ш

sh(или Shell Command Language) - это язык программирования, описанный стандартом POSIX . Она имеет множество реализаций ( ksh88, dash, ...). bashтакже может рассматриваться как реализация sh(см. ниже).

Потому shчто это спецификация, а не реализация, /bin/shэто символическая ссылка (или жесткая ссылка) на фактическую реализацию в большинстве систем POSIX.

Что такое bash

bashначиналась как sh-совместимая реализация (хотя она предшествует стандарту POSIX на несколько лет), но со временем она приобрела множество расширений. Многие из этих расширений могут изменить поведение допустимых сценариев оболочки POSIX, поэтому сама по себе bashне является допустимой оболочкой POSIX. Скорее, это диалект языка оболочки POSIX.

bashподдерживает --posixпереключатель, что делает его более POSIX-совместимым. Он также пытается имитировать POSIX, если вызывается как sh.

ш = баш?

Долгое время /bin/shиспользовался для указания /bin/bashна большинство систем GNU / Linux. В результате почти стало безопасным игнорировать разницу между ними. Но это начало меняться в последнее время.

Вот некоторые популярные примеры систем /bin/sh, на которые нет указаний /bin/bash(а на некоторых из которых они /bin/bashмогут даже не существовать):

  1. Современные системы Debian и Ubuntu, ссылки shна которые dashпо умолчанию установлены;
  2. Busybox , который обычно запускается во время загрузки системы Linux как часть initramfs. Он использует ashреализацию оболочки.
  3. BSD и вообще любые не-Linux системы. OpenBSD использует pdksh, потомок оболочки Korn. FreeBSD shявляется потомком оригинальной оболочки UNIX Bourne. У Solaris есть свой собственный, shкоторый долгое время не был POSIX-совместимым; бесплатная реализация доступна из проекта Heirloom .

Как вы можете узнать, на что /bin/shуказывает ваша система?

Сложность в том, что это /bin/shможет быть символическая ссылка или жесткая ссылка. Если это символическая ссылка, портативный способ решить это:

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

Если это жесткая ссылка, попробуйте

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

На самом деле -Lфлаг охватывает как символические, так и жесткие ссылки, но недостатком этого метода является то, что он не переносим - POSIX не требует find поддержки -samefileопции, хотя и GNU find, и FreeBSD find поддерживают ее.

Линия Шебанг

В конечном счете, вам решать, какой из них использовать, написав строку «шебанг».

Например

#!/bin/sh

будет использовать sh(и все, что происходит, чтобы указать),

#!/bin/bash

будет использовать, /bin/bashесли это доступно (и не с сообщением об ошибке, если это не так). Конечно, вы также можете указать другую реализацию, например,

#!/bin/dash

Какой использовать

Для моих собственных сценариев я предпочитаю shпо следующим причинам:

  • это стандартизировано
  • это намного проще и легче учиться
  • он переносим через системы POSIX - даже если они не имеют bash, они должны иметьsh

Есть и преимущества в использовании bash. Его особенности делают программирование более удобным и похожим на программирование на других современных языках программирования. К ним относятся такие вещи, как локальные переменные и массивы. Plain sh- очень минималистичный язык программирования.

Hunter.S.Thompson
источник
Спасибо за подробный ответ: я использую Linux Mint, который основан на Debian и /bin/shдействительно является символической ссылкой на /bin/dash.
Макс Уильямс
Если вы хотите придерживаться POSIX sh, тогда лучше не указывать шебанг
Даниэль Жур
4
@DanielJour Если бы любая Unix-подобная система потеряла обычную поддержку shebangs, это сломало бы столько вещей, что было бы практически непригодным для использования. Так что это стандарт де-факто, даже если он не указан в POSIX. Единственная проблема совместимости заключается в том, что путь к интерпретаторам может отличаться.
Бармар
23

В дополнение к отличному ответу от @ Hunter.S.Thompson, я хотел бы отметить, что непереносимая часть сценария

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

Расширение ${variable/search/replace}GNU. Но вы можете легко избежать этого с чистым POSIX:

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

Вслед за Хантером это лучшее решение, чем изменение Шебанга на #! /bin/bash

Philippos
источник
Спасибо - я согласен, что это "более чистое" исправление, но я бы предпочел загрузку bash с помощью shebang, так как bash - это то, что я обычно использую в терминале (по умолчанию), и проще, когда скрипты делают то же самое, что и обычные командная строка.
Макс Уильямс
1
@MaxWilliams Конечно, нет проблем. Это было в основном с отступом для базы данных вопросов и ответов.
Филиппос