заголовки сценария оболочки (#! / bin / sh vs #! / bin / csh)

92

Почему все файлы скриптов начинаются с

#!/bin/sh

или с

#!/bin/csh

Это требуется? Какая у этого цель? И в чем разница между ними?

Раз два три
источник
1
Для сценария csh вы должны использовать #!/bin/csh -f; -fговорит оболочке не к источнику пользователя .loginи .cshrc, что делает скрипт запущен быстрее и избежать зависимостей от настройки пользователя. (Или еще лучше, не пишите сценарии csh.) Не используйте -fдля сценариев sh или bash; это не имеет того же значения.
Кейт Томпсон

Ответы:

99

Это известно как Shebang:

http://en.wikipedia.org/wiki/Shebang_(Unix)

#! интерпретатор [необязательный-аргумент]

Shebang имеет значение, только если у сценария есть разрешение на выполнение (например, chmod u + x script.sh).

Когда оболочка выполняет сценарий, она будет использовать указанный интерпретатор.

Пример:

#!/bin/bash
# file: foo.sh
echo 1

$ chmod u+x foo.sh
$ ./foo.sh
  1
Kwarrick
источник
@Kolob Canyon вам не нужно, но он может помочь некоторым редакторам с подсветкой синтаксиса (хотя обычно есть и другие способы добиться того же): unix.stackexchange.com/a/88730/193985
Брахам Снайдер,
42

#!Строка сообщает ядро ( в частности, об осуществлении execveсистемного вызова) , что эта программа написана на интерпретируемых языках; следующий за ним абсолютный путь идентифицирует интерпретатор. Программы, скомпилированные в машинный код, начинаются с другой последовательности байтов - в большинстве современных Unix-систем 7f 45 4c 46(^?ELF ), которая идентифицирует их как таковые.

Вы можете указать абсолютный путь к любой программе после символа #!, если эта программа сама по себе не является #!сценарием. Ядро перезаписывает вызов

./script arg1 arg2 arg3 ...

где ./scriptначинается, скажем, #! /usr/bin/perlкак если бы командная строка действительно была

/usr/bin/perl ./script arg1 arg2 arg3

Или, как вы видели, вы можете использовать #! /bin/shдля написания сценария, предназначенного для интерпретации sh.

#!Строка обрабатывается только , если вы непосредственно вызывать скрипт ( ./scriptв командной строке); файл также должен быть исполняемым ( chmod +x script). Если вы линия не является необходимым (и будет игнорироваться , если он присутствует), и файл не должен быть исполняемым. Точка в функции, чтобы позволить вам непосредственно вызывать программы истолковано языка , не зная , на каком языке они написаны. (Dosh ./script#!grep '^#!' /usr/bin/* - вы обнаружите , что очень многие фондовые программы, на самом деле , используя эту функцию.)

Вот несколько правил использования этой функции:

  • Это #!должны быть самые первые два байта в файле. В частности, файл должен быть в кодировке, совместимой с ASCII (например, UTF-8 будет работать, а UTF-16 - нет) и не должен начинаться с «метки порядка байтов», иначе ядро ​​не распознает его как #!сценарий.
  • Путь после #!должен быть абсолютным (начинается с /). Он не может содержать символы пробела, табуляции или новой строки.
  • Это хороший стиль, но не требуется, чтобы поставить пробел между #!и /. Не помещайте здесь более одного места.
  • Вы не можете помещать переменные оболочки в #!строку, они не будут расширены.
  • Вы можете поместить один аргумент командной строки после абсолютного пути, отделенный от него одним пробелом. Как и абсолютный путь, этот аргумент не может содержать символы пробела, табуляции или новой строки. Иногда это необходимо для того, чтобы все работало ( #! /usr/bin/awk -f), иногда просто полезно ( #! /usr/bin/perl -Tw). К сожалению, после абсолютного пути нельзя поставить два или более аргумента.
  • Некоторые люди скажут вам использовать #! /usr/bin/env interpreterвместо #! /absolute/path/to/interpreter. Это почти всегда ошибка. Это делает поведение вашей программы зависимым от $PATHпеременной пользователя, который запускает скрипт. И не все системы envв первую очередь.
  • Программы, которые нужны setuidили setgidкоторые не могут использовать привилегии #!; они должны быть скомпилированы в машинный код. (Если вы не знаете, что это setuidтакое, не беспокойтесь об этом.)

Что касается csh, это shпримерно так же, как Nutrimat Advanced Tea Substitute для чая. Он имеет (или, скорее, имел; современные реализации shдогнали) ряд преимуществ перед shинтерактивным использованием, но использование его (или его потомка tcsh) для написания сценариев почти всегда является ошибкой . Если вы новичок в написании сценариев оболочки в целом, я настоятельно рекомендую вам игнорировать его и сосредоточиться на sh. Если вы используете cshродственника в качестве оболочки для входа в систему, переключитесь на bashили zsh, чтобы интерактивный командный язык был таким же, как язык сценариев, который вы изучаете.

Zwol
источник
Последние версии Linux позволяют указанному интерпретатору быть сценарием. Обычная практика - опускать пробел после #!; Без комментариев, хороший ли это стиль. См. Этот вопрос и мой ответ для обсуждения плюсов и минусов #!/usr/bin/envвзлома.
Кейт Томпсон,
@KeithThompson У меня сложилось впечатление, что Linux - единственный распространенный вариант Unix, который позволяет интерпретатору быть сценарием, так что на него все равно не полагаться. С тех пор, как я написал это, я сам столкнулся с ситуацией, когда все #!/usr/bin/envбыло правильно, но я по-прежнему считаю, что это почти всегда плохая идея.
zwol
4

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

Когда вы вводите команду в командной строке Unix, вы взаимодействуете с оболочкой.

Например, #!/bin/cshотносится к C-shell, /bin/tcsht-shell, /bin/bashbash shell и т. Д.

Вы можете сказать, какую интерактивную оболочку вы используете

 echo $SHELL

команда, или альтернативно

 env | grep -i shell

Вы можете изменить свою командную оболочку с помощью chshкоманды.

У каждого из них немного другой набор команд и способ назначения переменных, а также собственный набор программных конструкций. Например, оператор if-else с bash отличается от оператора C-оболочки.

Эта страница может быть интересна, поскольку она «переводит» между командами / синтаксисом bash и tcsh.

Использование директивы в сценарии оболочки позволяет запускать программы с использованием другой оболочки. Например, я использую tcshоболочку в интерактивном режиме, но часто запускаю сценарии bash, используя / bin / bash в файле сценария.

В сторону:

Эта концепция распространяется и на другие скрипты. Например, если вы программируете на Python, вы должны поместить

 #!/usr/bin/python

в верхней части вашей программы Python

Левон
источник
Так это обязательно? Как узнать, какую оболочку я использую на самом деле?
One Two Three
Так что, если я пишу сценарии для кого-то, чтобы использовать их на своей машине, и я не знаю, какую оболочку они используют. (Этот человек, к сожалению, ничего не знает об этом, поэтому все, что он может сделать, это запустить сценарий, ничего не меняя). Могу я сделать что-нибудь вроде #! $SHELL? Будет ли это поместить в Shebang правильную оболочку?
One Two Three
1
@OneTwoThree. Большинство систем имеют стандартные оболочки, если вы напишете сценарий bash или csh, все будет в порядке. Какую оболочку они используют в интерактивном режиме, не имеет значения, в этом прелесть !#/bin/bashдирективы eg ,. Он сообщает системе, какую оболочку использовать для выполнения сценария оболочки.
Левон
Значение $SHELLне обязательно говорит вам, какую оболочку вы используете в данный момент; обычно он сообщает вам оболочку по умолчанию . tcsh устанавливает $versionи $tcsh; наборы bash $BASH_VERSION. Не все снаряды обязательно имеют одинаковые механизмы.
Кейт Томпсон
1
@OneTwoThree: #!строка должна соответствовать синтаксису сценария, а не интерактивной оболочке, используемой тем, кто запускает сценарий.
Кейт Томпсон