Существует ли команда для выполнения скрипта согласно его shebang строке?

Вопрос или проблема

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

bash script.sh

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

Да. Это называется perl. Некоторые примеры с соответствующим интерпретатором в строке shebang файла (фактическое расширение файла не имеет значения):

perl foo.bash    # работает
perl foo.lua     # работает
perl foo.clisp   # работает
perl foo.csh     # работает
perl foo.php     # работает
perl foo.gnuplot # работает (без аргументов)
perl foo.pl      # работает (очевидно)
perl foo.py      # работает
perl foo.sh      # работает
perl foo.tcl     # работает
perl foo.rb      # работает
perl foo.nodejs  # работает
perl foo.r       # работает
perl foo.oct     # работает
perl foo.csharp  # работает (без аргументов)

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

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

Скрипты не обязательно имеют shebang

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

Ответ, следовательно, нет, нет команды, которая точно определит, какой язык (интерпретатор) использовать для запуска скрипта. Однако вы всегда можете заглянуть внутрь скрипта и узнать, есть ли у него shebang.

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

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

Если у скрипта нет shebang, однако, нет (прямой*) информации внутри скрипта, чтобы узнать, какой интерпретатор использовать.

Тем не менее

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

Пример

#!/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 скриптов.

Объяснение

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

Примечание

Расширение (.sh, .py и т.д.) не играет никакой роли в определении подходящего интерпретатора в Linux.


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

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

#!/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). Фактически процесс, вызывающий скрипт напрямую, не знает о #! — ядро использует это, чтобы понять, что ему нужно запустить интерпретатор.

Ядро linux уже реализовало этот запрос. Вам нужно просто:

chmod u+x <имя файла>
<имя файла>

Можно сделать то же самое из любого контекста: c, python, bash и т.д.

Ответ или решение

Исполнение скрипта в соответствии с shebang

Вариант запуска скрипта в Unix-подобных системах, не имеющего прав на исполнение, может быть решен различными способами. Один из часто встречающихся вопросов — как выполнить скрипт, не зная его интерпретатора, при этом не устанавливая ему права на выполнение. В данной статье мы рассмотрим, какие подходы можно использовать для решения этой задачи.

Понимание Shebang

Shebang (или "шершафт") — это первая строка в скрипте, начинающаяся с символов #!, за которыми следует путь к интерпретатору. Например, строка #!/bin/bash указывает на то, что скрипт должен быть выполнен с помощью Bash. Но при этом не все скрипты имеют shebang, и если вы пытаетесь запустить скрипт без выполнения с интерпретатором, то система может не понять, каким образом его исполнять.

Запуск скрипта с заданным интерпретатором

Если вы уверены, что знаете, какой интерпретатор использовать (например, Bash, Python и т.д.), то вы можете явно задать его следующим образом:

bash script.sh

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

Поиск интерпретатора через Shebang

Однако, если вы не знаете, каким интерпретатором следует выполнять скрипт, можно использовать другой подход, например, создать завершающий (wrapper) скрипт на Python. Вот пример такого решения:

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

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

try:
    with open(script) as f:
        first_line = f.readline().strip()
        if first_line.startswith("#!"):
            interpreter = first_line.split()[1]
            cmd = [interpreter, script] + args[1:]
            subprocess.call(cmd)
except (PermissionError, FileNotFoundError, IndexError):
    print("Не найден корректный shebang.")
  1. Способность читать shebang: Этот скрипт открывает указанный файл, считывает первую строку и определяет интерпретатор.
  2. Запуск скрипта: Если она начинается с #!, то интерпретатор запускается с аргументами.

Сохраните скрипт как tryrun, сделайте его исполняемым, добавьте в PATH и запустите:

tryrun /path/to/nonexecutablescript

Таким образом, вы сможете запустить неисполняемые скрипты, извлекая из них интерпретатор.

Ограничения и предостережения

  1. Неявные зависимости: Если скрипт не содержит shebang, может возникнуть неопределенность относительно его интерпретатора, что делает необходимость ручной проверки кода. Это может быть проблемой для менее известных или самописных языков программирования.

  2. Безопасность: Создание оберток для исполнения скриптов с недостаточными правами может нести риски безопасности. Распространение неисполняемых файлов может приводить к запуску потенциально вредоносного кода.

  3. Учет прав доступа: Перед модификацией прав доступа всегда учитывайте, что они предназначены для защиты вашей системы от несанкционированного исполнения.

Заключение

Да, существует способ запуска скрипта согласно ее shebang, но это требует вмешательства и разработки дополнительного инструмента. Необходимо внимательно исследовать скрипты на наличие shebang перед их исполнением и осознавать риски, связанные с правами доступа и безопасностью.

Четкое понимание работы интерпретаторов и shebang является важным для разработки и поддержки скриптов в среде Unix.

Оцените материал
Добавить комментарий

Капча загружается...