Скидки до 28% + 2-ая профессия бесплатно и подарки на 50 000₽

Главная | Все статьи | Дневник студента

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

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

Я насчитал 10 способов запуска. И три разных 'echo' на одной убунте. И узнал некоторые трюки. Пока искал, как перекинуть изменённую переменную из дочернего процесса в родительский.

Встраивание сценария в текущий процесс 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), похоже, такого не умеет.

Читайте также: Как использовать коды завершения в Bash-скриптах

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

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

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
Похожие статьи
Рекомендуемые программы
профессия
Верстка на HTML5 и CSS3, Программирование на JavaScript в браузере, разработка клиентских приложений используя React
10 месяцев
с нуля
Старт 26 декабря
профессия
Программирование на Python, Разработка веб-приложений и сервисов используя Django, проектирование и реализация REST API
10 месяцев
с нуля
Старт 26 декабря
профессия
Тестирование веб-приложений, чек-листы и тест-кейсы, этапы тестирования, DevTools, Postman, SQL, Git, HTTP/HTTPS, API
4 месяца
с нуля
Старт 26 декабря
профессия
Программирование на Java, Разработка веб-приложений и микросервисов используя Spring Boot, проектирование REST API
10 месяцев
с нуля
Старт 26 декабря
профессия
новый
Google таблицы, SQL, Python, Superset, Tableau, Pandas, визуализация данных, Anaconda, Jupyter Notebook, A/B-тесты, ROI
9 месяцев
с нуля
Старт 26 декабря
профессия
Программирование на PHP, Разработка веб-приложений и сервисов используя Laravel, проектирование и реализация REST API
10 месяцев
с нуля
Старт 26 декабря
профессия
Программирование на Ruby, Разработка веб-приложений и сервисов используя Rails, проектирование и реализация REST API
5 месяцев
c опытом
Старт 26 декабря
профессия
Программирование на JavaScript в браузере и на сервере (Node.js), разработка бекендов на Fastify и фронтенда на React
16 месяцев
с нуля
Старт 26 декабря
профессия
Программирование на JavaScript, разработка веб-приложений, bff и сервисов используя Fastify, проектирование REST API
10 месяцев
с нуля
Старт 26 декабря
профессия
новый
Git, JavaScript, Playwright, бэкенд-тесты, юнит-тесты, API-тесты, UI-тесты, Github Actions, HTTP/HTTPS, API, Docker, SQL
8 месяцев
c опытом
Старт 26 декабря