Весенние скидки до 30 000 ₽
На все профессии до 31 марта
Главная | Все статьи | Дневник студента

Сколько способов исполнить скрипт bash?

Время чтения статьи ~4 минуты
Статья написана студентом Хекслета. Мнение автора может не совпадать с позицией редакции
Сколько способов исполнить скрипт bash? главное изображение

Встраивание сценария в текущий процесс bash


Когда мы вызываем скрипт из терминала — то этот скрипт становится отдельным процессом, дочерним от процесса терминала. Он наследует все переменные среды, которые были объявлены через export $VAR (а не просто $VAR — простые не будут видны.) Когда этот дочерний скрипт изменяет унаследованную переменную — эта переменная уже его лично, в родительском процессе значение переменной не меняется. Надёжный способ передать из дочернего процесса в родительский какую-то инфу — это возвратить какие-то результаты, как из функции, или послать вывод дочерней функции в пайп.

bash ввёл такую тему: «встраивание» кода, прочитанного из другого файла прямо в текущий процесс. Файл не обязан быть исполняемым, а по правилам хорошего тона — его таким и не делают.

Примеры таких команд:

. other_file
source other_file  #это полный синоним точки, создан для понятности
source .venv/bin/activate  #помните такое?
source .bashrc  #применяем изменения, когда обновили .bashrc
source .profile  #применяем изменения, когда обновили .profile

Это позволяет исполнить посторонний код, он обновляет переменные, а текущий процесс этим пользуется. Если бы посторонний код был запущен как дочерний — это не получилось бы. Если подключаемы скрипт захочет навредить — он навредит. Если в нём exit — то весь процесс закроется, не отработав до конца.

В подключаемом скрипте могут быть только переменные и функции — тогда переменные запишутся в текущее окружение, а функции будут доступны для вызова. Если и в текущем и в новом скрипте есть функции с одинаковым именем — нужно сперва текущую переназвать declare -f new_function_name=old_function_name, а потом делать вставку source side_bashcode.

Говорят, что ради возможности загрузить переменные из посторонних источников это и сделали. Ради того, чтоб у каждого пользователя был свой личный .profile (вызывается при логине) и .bashrc (вызывается при создании терминала). Это не синонимы. Окошко с терминалом можно закрыть, или открыть второе — .bashrc прочтётся заново. Даже без GUI — команда exec $SHELL делает то же самое, заставляет заменить текущий процесс командной оболочки на новый процесс, возможно даже на другое приложение. Оно запустится с нуля, как будто после логина. Но без самого логина, без перезагрузки .profile. Зато перезагрузится .bashrc.

Оригинальный, более старый и «совместимый со всеми» shell (sh), похоже, такого не умеет.

Запуск дочернего процесса


Это «обычный» запуск. Когда запускаем скрипт по имени, или приложение — то «родительская» (та, из которой исходит команда) прилога создаёт копию своего процесса, грузит в процесс код из запускаемого приложения, и «передаёт управление» = прекращает посылать команды, начинает с команды новой прилоги, которая описана как «точка входа». Примеры:

sh script.sh
bash script.sh
~/script.sh

Чтобы вызвать так, нужно поставить разрешение «execute». Если это не машкод, а текст — в нём указать первой строкой #!/path/to/interpreter. Если хочешь вызывать его из любого каталога — добавь его в одну из папок $PATH, или папку с ним добавь в $PATH. Вуаля, системная команда, как у взрослых!

Дочка может унаследовать права от матери. Если найдёт себе материнский процесс, работающий под именем root, и тот позволит отпочковаться с теми же правами — вау, у процесса права на всё!

Какие ещё способы запуска?


#!/bin/bash
SCRIPT_PATH="/path/to/script.sh"

# Insert
. "$SCRIPT_PATH"

# Insert
source "$SCRIPT_PATH"

# Fork
"$SCRIPT_PATH"

# Fork
bash "$SCRIPT_PATH"

# ?
eval '"$SCRIPT_PATH"'

# ?
OUTPUT=$("$SCRIPT_PATH")
echo $OUTPUT

# ?
OUTPUT=`"$SCRIPT_PATH"`
echo $OUTPUT

# обратные кавычки тоже запускают скрипт!
./script-that-consumes-argument.sh `sh script-that-produces-argument.sh`

# ?
("$SCRIPT_PATH")

# ?
(exec "$SCRIPT_PATH")

Вариант с двумя файлами:

# cat showdate.sh
#!/bin/bash
echo "Date is: `date`"

# cat mainscript.sh
#!/bin/bash
echo "You are login as: `whoami`"
echo "`/bin/sh ./showdate.sh`" # exact path for the script file

# ./mainscript.sh
You are login as: root
Date is: Thu Oct 17 02:56:36 EDT 2013

& в конце — он позволит форкнуть процесс и тут же отключить его от терминала. Процесс работает сам по себе, а мы его не ждём, можем вводить новые команды или запускать параллельные процессы.

./myscript.sh &

echo


Внимейшн! Я узнал три варианта echo:

  • echo в sh — команда, которая не понимает опции -e (она её просто печатает как будто это целевой текст), но зато понимает, что \n (и другие эскейпы) нужно превращать в спецсимволы;
  • echo в bash — команда, которая без опции -e не превращает \n и другие эскейпы, ей нужно прямо заказать опцию -e — и тогда начнёт превращать;
  • echo в GNU utilites — вообще самостоятельная прилога, у неё тоже какие-то особенности.

В общем, советы в интернете противоречат друг другу, проще прочитать man echo о том варианте, который используете вы.

Записать переменную в файл


var="text to append";
destfile=/some/directory/path/filename

if [ -f "$destfile" ]
then 
    echo "$var" > "$destfile"
fi

Попроще:

echo "$var" > "$destfile"

printf работает как в C:

printf "%s" "$var" > "$destfile"

Эти ответы не сработают, если текст:

  • начинается с -e, или с -n, или с -E
  • или содержит \n
  • не должен заканчиваться переводом строки, когда будет записан в файл.

Что тогда поможет?

cat <<< "$var" > "$destfile"
printenv var > "$destfile"

printenv


printenv [name] -- print out the environment [or just only name value if specified] env [-] [-i] [name=value ...] [utility [argument ...]] -- set and print environment

Я видел одно упоминание этой команды, а команда есть!

Переменные в bash

Аватар пользователя Alex Sweetall
Alex Sweetall 11 мая 2021
1
Рекомендуемые программы
профессия
от 6 300 ₽ в месяц
Разработка фронтенд-компонентов для веб-приложений
10 месяцев
с нуля
Старт 4 апреля
профессия
от 6 300 ₽ в месяц
Разработка веб-приложений на Django
10 месяцев
с нуля
Старт 4 апреля
профессия
от 6 183 ₽ в месяц
Ручное тестирование веб-приложений
4 месяца
с нуля
Старт 4 апреля
профессия
от 6 300 ₽ в месяц
Разработка приложений на языке Java
10 месяцев
с нуля
Старт 4 апреля
профессия
от 5 025 ₽ в месяц
новый
Сбор, анализ и интерпретация данных
9 месяцев
с нуля
Старт 4 апреля
профессия
от 6 300 ₽ в месяц
Разработка веб-приложений на Laravel
10 месяцев
с нуля
Старт 4 апреля
профессия
от 5 840 ₽ в месяц
Создание веб-приложений со скоростью света
5 месяцев
c опытом
Старт 4 апреля
профессия
от 9 900 ₽ в месяц
Разработка фронтенд- и бэкенд-компонентов для веб-приложений
16 месяцев
с нуля
Старт 4 апреля
профессия
от 6 300 ₽ в месяц
Разработка бэкенд-компонентов для веб-приложений
10 месяцев
с нуля
Старт 4 апреля
профессия
новый
Автоматизированное тестирование веб-приложений на JavaScript
8 месяцев
c опытом
в разработке
Старт 4 апреля
профессия
Верстка с использованием последних стандартов CSS
5 месяцев
с нуля
Старт в любое время