Як визначити скрипт оболонки, який потрібно не запускати


40

Я визначаю скрипт оболонки, який користувач повинен sourceзамість виконувати.

Чи є звичайний чи розумний спосіб натякнути користувачеві, що це так, наприклад, через розширення файлу?

Чи є код оболонки, який я можу записати у сам файл, що призведе до того, що воно повторить повідомлення та закриє його, якщо воно виконується замість джерела, щоб я міг допомогти користувачеві уникнути цієї очевидної помилки?


1
Отже, якщо користувач пише однорядковий скрипт оболонки x, який просто містить команду . your-script-to-be-sourced, це нормально, але якщо він хоче виконати, bash your-script-to-be-sourcedце слід заборонити? У чому сенс цього обмеження?
— користувач1934428

8
@ user1934428 Звичайно. Це нормально для сценарію, який обчислює кількість envзмінних і залишає їх як фактичний вихід сценарію. Новачок застряже цілими днями на головоломці, якщо ви дозволите їх виконувати.
— kubanczyk

Відповіді:


45

Припускаючи, що ви використовуєте bash, поставте наступний код біля початку сценарію, який ви хочете отримати, але не виконати:

if [ "${BASH_SOURCE[0]}" -ef "$0" ]
then
    echo "Hey, you should source this script, not execute it!"
    exit 1
fi

У розділі bash ${BASH_SOURCE[0]}міститиметься ім'я поточного файлу, який читає оболонка, незалежно від того, отримано чи виконано.

Навпаки, $0це ім'я поточного файлу, який виконується.

-efперевіряє, чи ці два файли є одним файлом. Якщо вони є, ми попереджаємо користувача та виходимо.

Ні, -efні BASH_SOURCEPOSIX. Хоча -efпідтримується ksh, yash, zsh та Dash, BASH_SOURCEпотрібен bash. Вzsh той же час ${BASH_SOURCE[0]}може бути замінений на ${(%):-%N}.


2
Просто echo "Usage: source \"$myfile\""
— kubanczyk

6
@kubanczyk sourceне є портативним. Враховуючи, що ця відповідь є специфічною для .
— удару

33

Невиконаний файл може знаходитись, але не виконуватись, тому, як перший рядок захисту, не встановлення прапорця, що виконується, повинен бути хорошим підказом ...

Редагувати: хитрість, на яку я щойно натрапив: зробіть шебанг будь-яким виконуваним файлом, який не є інтерпретатором оболонки, /bin/falseзмушує сценарій повертати помилку (rc! = 0)

#!/bin/false "This script should be sourced in a shell, not executed directly"

7
Однак невиконаний файл все ще може бути виконаний через, наприклад, bash somefile.sh...
— twalberg

1
Якщо ви знаєте, що можете виконати це за допомогою bash(vd perl, python, awk ...), тоді ви подивилися джерело та побачили коментар, який говорить, що цього не роблять :)
— xenoid

1
Сценарії Perl, як правило, названі somefile.plі Python як somefile.py, так що ні, я, мабуть, не читав коментарів (які це, навіть, у будь-якому випадку?), І bash somefile.shвін коротший, ніж chmod +x somefile.sh; ./somefile.sh...
— twalberg

Також деякі оболонки, подібні до Борна, в тому числі bash, спершу спробують execveфайл, але якщо це не вдасться, вони перевіряють файл вручну та вручну інтерпретують #!і викликають його через цей інтерпретатор: це спадщина з тих часів, коли #!була просто область користувачів конвенція, замість того, щоб керувати самим ядром. Я думаю bash , щонайменше, це не зробить для невиконаних файлів, але я не знаю, чи можна переносити очікування такої розумної поведінки від усіх оболонок, від яких користувач може викликати сценарій.
— mtraceur

"Якщо ви знаєте, що можете виконати це за допомогою bash" Гм, ні, інколи користувач просто не здогадується, що існують інші оболонки, ніж bash це bash script.shможе бути небезпечно.
— Сергій Колодяжний

10

У цій публікації про переповнення стека запропоновано кілька методів , з яких мені сподобався функціональний метод, запропонований Wirawan Purwanto та mr.spuratic best:

Найбільш надійний спосіб, як запропонував Wirawan Purwanto, - це перевірити FUNCNAME[1] функцію :

function mycheck() { declare -p FUNCNAME; }
mycheck

Потім:

$ bash sourcetest.sh
declare -a FUNCNAME='([0]="mycheck" [1]="main")'
$ . sourcetest.sh
declare -a FUNCNAME='([0]="mycheck" [1]="source")'

Це еквівалент перевірки результатів caller, значень mainта sourceрозрізнення контексту виклику. Використання FUNCNAME[]дозволяє заощаджувати та аналізувати callerвихід. Вам потрібно знати або обчислити глибину локального дзвінка, щоб бути правильною. Випадки, такі як сценарій, що надходить з іншої функції або сценарію, призведуть до того, що масив (стек) буде глибшим. ( FUNCNAMEце спеціальна змінна масив bash, вона повинна мати суміжні індекси, що відповідають стеку викликів, доки це ніколи unset.)

Тож ви можете додати до початку сценарію:

function check()
{
    if [[ ${FUNCNAME[-1]} != "source" ]]   # bash 4.2+, use ${FUNCNAME[@]: -1} for older
    then
        printf "Usage: source %s\n" "$0"
        exit 1
    fi
}
check

7

Якщо припустити, що сценарій виконувати сценарій просто марно, а не шкідливо, ви можете додати

return 0 || printf 'Must be sourced, not executed\n' >&2

до кінця сценарію. returnпоза функцією має ненульовий код виходу, якщо файл не отримується.


3
Зауважте, що це повертає 0 статус виходу. Спробуйте цей подібний ідіом, який я використовую замість цього:return 2>/dev/null; echo "$0: This script must be sourced" 1>&2; exit 1
— wjandrea

5

Коли ви подаєте джерело сценарію оболонки, рядок shebang ігнорується. Ввівши недійсний шебанг, ви можете попередити користувача, що сценарій був помилково виконаний:

#!/bin/bash source-this-script
# ...

Повідомлення про помилку буде таким:

/bin/bash: source-this-script: No such file or directory

Назва (довільного) аргументу вже дає чіткий натяк, але повідомлення про помилку все ще не на 100% ясно. Ми можемо це виправити за допомогою скрипта утиліти, source-this-scriptякий розміщується десь у вашому PATH:

#!/bin/sh
echo >&2 "This script must be sourced, not executed${1:+: }${1:-!}"
exit 1

Тепер повідомлення про помилку буде таким:

This script must be sourced, not executed: path/to/script.sh

Порівняння з іншими підходами

У порівнянні з іншими відповідями, цей підхід вимагає лише мінімальних змін до кожного сценарію (а наявність рядка shebang допомагає у виявленні типу файлів у редакторах та вказує діалект сценарію оболонки, тому є навіть переваги). Недоліком є ​​дещо незрозуміле повідомлення про помилку або (разове) додавання іншого сценарію оболонки.

bash path/to/script.shОднак це не заважає явному виклику через (спасибі @muru!).


1
Ще один недолік полягає в тому, що це не захистить від цього bash some/script.sh, що також ігнорує шебанг.
— муру

4
Ви можете зробити повідомлення більш зрозумілим, зробивши шебанг #!/bin/echo 'You must source this script!'чи щось подібне.
— Кріс

1
@Chris: Правильно, але тоді я втратив би виявлення типу файлу (наприклад, у Vim) та документації, який це діалект оболонки. Якщо вам це не байдуже, ваша пропозиція справді позбудеться другого сценарію!
— Інго Каркат
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.