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


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.