Есть ли команда для запуска скрипта в соответствии со строкой shebang?

46

Если я хочу выполнить bash-скрипт, для которого не установлено разрешение на выполнение, я могу сделать:

bash script.sh

Что я должен использовать вместо, bashесли скрипт не является исполняемым, и я не знаю правильный интерпретатор? Есть ли команда, которая ищет интерпретатор из строки shebang и выполняет сценарий вместе с ним?

Айвар
источник
Что случилось с Bash?
Штеффен
@steffen откуда вы знаете, что рассматриваемый файл является bash-скриптом?
Муру
@muru цитата из Вопроса: «Если я хочу выполнить скрипт bash ...» Более того (даже если это не скрипт bash), если bash whateverработает, зачем использовать что-то другое? bash доступен практически на всех * ix системах, так что зачем ...
Штеффен
1
@steffen ты прочитал остальную часть вопроса? Они говорят: «Если ... тогда я могу сделать: ...» и «Что я должен использовать вместо bash, если ... Я не знаю правильного переводчика
Муру
@ Муру: может быть, я не вижу очевидного. Но если в файле / есть / строка shebang, как указано в вопросе, bash сделает именно то, что запрашивается. Как будет perl, согласно ответу ниже. Так в чем же преимущество не использовать bash?
Штеффен

Ответы:

67

Ага. Это называется perl:

perl foo.bash    # works
perl foo.lua     # works
perl foo.clisp   # works
perl foo.csh     # works
perl foo.php     # works
perl foo.gnuplot # works (no arguments)
perl foo.pl      # works (obviously)
perl foo.py      # works
perl foo.sh      # works
perl foo.tcl     # works
perl foo.rb      # works
perl foo.nodejs  # works
perl foo.r       # works
perl foo.oct     # works
perl foo.csharp  # works (no arguments)

Это упоминается в документации Perl :

Если в #!строке нет ни слова «perl», ни слова «indir», #!вместо интерпретатора Perl выполняется программа, названная в честь . Это немного странно, но это помогает людям на машинах, которые этого не делают #!, потому что они могут сообщить программе, что их SHELL - / usr / bin / perl, и Perl затем отправит программу нужному для них интерпретатору.

Оле Танге
источник
40
Ну, это просто грязно.
Slim
1
Расширение файла .jsтоже работает?
Пизис
1
Кроме того, что машины не делают #!. Я, кажется, немногим больше, и не сталкивался с этой проблемой.
Пизис
1
Хорошо, это просто ваш пример, который, кажется, выдвигает на первый план множество расширений файлов, а не демонстрирует несколько строк shebang из файлов, и я предполагаю, что предположил, что именно так его предсказание сработало.
Пизис
2
Мне нравится, как man perlrunзастенчиво признает, что это "немного странно" :). Я думаю, что это следует рассматривать как любопытство, направленное на не-UNIX среды и очень очень старые версии UNIX.
Слим
25

Сценарии не обязательно имеют шебанг

Если скрипт был запущен от переводчика, Вы не можете быть уверены, что он имеет шебанг вообще . Скрипты, запускаемые из интерпретатора, не нуждаются в шебанге , если вы вызываете интерпретатор для запуска кода.

Таким образом, ответ - нет, нет команды, которая бы точно знала, на каком языке (интерпретаторе) запускать скрипт. Однако вы всегда можете заглянуть внутрь сценария и посмотреть, есть ли у него шебанг, чтобы узнать.

Правила вкратце:

  1. Когда вы запускаете скрипт, вызов интерпретатора всегда отменяет возможные shebang, исполняемые или нет, shebang или нет.
  2. Если скрипт не исполняется и запускается из интерпретатора, сценарий не нуждается в шебанге.
  3. Если скрипт запускается без предварительного вызова интерпретатора, ему нужен (и использует) шебанг, чтобы узнать, какой интерпретатор вызывать, и он должен быть исполняемым, чтобы иметь «разрешение» на вызов интерпретатора из своего шебанга.

Однако, если в сценарии нет шебанга, в сценарии нет (прямой *) информации, указывающей, какой интерпретатор использовать.

Было сказано, что

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

Пример

#!/usr/bin/env python3
import subprocess
import sys

args = sys.argv[1:]; script = args[0]

try:
    lang = open(script).readlines()[0].replace("#!", "").strip().split()[-1]
    cmd = [lang, script]+args[1:]
    subprocess.call(cmd)
except (PermissionError, FileNotFoundError, IndexError):
    print("No valid shebang found")
  • Сохраните его как tryrunв $PATH(например ~/bin, сделайте каталог, если он не существует, выйдите из системы и войдите обратно), сделайте его исполняемым . Затем работает:

    tryrun /path/to/nonexecutablescript

    вызывает (проверял) правильный интерпретатор моих неисполняемых файлов pythonи bashскриптов.

объяснение

  • Сценарий просто читает первую строку сценария, удаляет #!и использует остальное для вызова интерпретатора.
  • Если он не может вызвать допустимого интерпретатора, он вызовет либо a, PermissionErrorлибо a FileNotFoundError.

Запись

Расширение ( .shи .pyт. Д.) Не играет никакой роли при определении соответствующего интерпретатора в Linux.


(* Конечно, можно разработать «умный» алгоритм предположения для определения синтаксиса из кода.)

Якоб Влейм
источник
Итак, это означает, что, хотя в Linux где-то реализовано извлечение shebang (чтобы он мог выбрать правильный интерпретатор для исполняемых srcipts), оно не предоставляется в качестве отдельной стандартной команды.
Айвар
@ Айвар, извлечение шебанга не проблема, но запуск кода без него вполне возможен.
Джейкоб Влийм
@ Айвар Ах, я понимаю, что ты имеешь в виду. Если сценарий является исполняемым и запускается без языка в команде, сценарий вызывает интерпретатор, а не наоборот.
Джейкоб Влейм
1
@JacobVlijm Я бы не сказал «скрипт вызывает интерпретатор», скорее «ядро Linux берет строку shebang для определения, какой интерпретатор вызывать при выполнении скрипта».
Paŭlo Ebermann
@ PaŭloEbermann Спасибо! Правда конечно. Ядро позаботится обо всей процедуре в любом случае, но, образно говоря, и я думаю, что для понимания лучше сказать, что скрипту «разрешено» заботиться о том, какой интерпретатор вызывать (и фактически делать это). Не уверен насчет формулировки, но я хотел бы описать ее так, как если бы инициатива была в сценарии, в то время как ядро ​​фактически выполняет эту работу.
Джейкоб Влейм
6

Вы можете достичь этого с помощью скрипта, подобного этому:

#!/bin/bash

copy=/tmp/runner.$$
cp $1 ${copy}
chmod u+x ${copy}
${copy}
rm ${copy}

Таким образом:

$ echo "echo hello" > myscript
$ ./myscript
bash: ./myscript: Permission denied
$ ./runscript myscript 
hello

Я рекомендую не делать этого. Разрешения есть по причине. Это программа для подрыва разрешений.

Обратите внимание, что обработка shebang - это функция ядра (в исходном коде Linux - fs/binfmt_script.c). По сути, процесс, вызывающий скрипт напрямую, не знает о #!- ядро ​​использует его, чтобы выяснить, что ему нужно запустить интерпретатор.

стройное
источник
2
Я всегда предполагал, что это была функция оболочки, а не ядра. Узнал что-то новое сегодня.
кодек
1
@boatcoder - Так как вы заинтересованы, добавили ссылку, где обрабатывает исходный код Linux.
стройная