Бесплатная книга-сайт на русском, полный гайд
Advanced Bash-Scripting Guide
Введение
BASH — Bourne-Again SHell (что может переводится как «перерожденный шел», или «Снова шел Борна(создатель sh)»), самый популярный командный интерпретатор в юниксоподобных системах, в особенности в GNU/Linux. Ниже приведу ряд встроенных команд, которые мы будем использовать для создания своих скриптов.
break
выход из цикла for, while или until
continue
выполнение следующей итерации цикла for, while или until
echo
вывод аргументов, разделенных пробелами, на стандартное устройство вывода
exit
выход из оболочки
export
отмечает аргументы как переменные для передачи в дочерние процессы в среде
hash
запоминает полные имена путей команд, указанных в качестве аргументов, чтобы не искать их при следующем обращении
kill
посылает сигнал завершения процессу
pwd
выводит текущий рабочий каталог
read
читает строку из ввода оболочки и использует ее для присвоения значений указанным переменным.
return
заставляет функцию оболочки выйти с указанным значением
shift
перемещает позиционные параметры налево
test
вычисляет условное выражение
times
выводит имя пользователя и системное время, использованное оболочкой и ее потомками
trap
указывает команды, которые должны выполняться при получении оболочкой сигнала
unset
вызывает уничтожение переменных оболочки
wait
ждет выхода из дочернего процесса и сообщает выходное состояние.
И конечно же кроме встроенных команд мы будем использовать целую кучу внешних, отдельных команд-программ, с которыми мы познакомимся уже в процессе
Что необходимо знать с самого начала
- Любой bash-скрипт должен начинаться со строки:
#!/bin/bash
в этой строке после #! указывается путь к bash-интерпретатору, поэтому если он у вас установлен в другом месте(где, вы можете узнать набрав whereis bash
) поменяйте её на ваш путь.
- Коментарии начинаются с символа # (кроме первой строки).
- В bash переменные не имеют типа(о них речь пойдет ниже)
Переменные и параметры скрипта
Приведу как пример небольшой пример, который мы разберем:
#!/bin/bash
#указываем где у нас хранится bash-интерпретатор
#присваиваем переменной parametr1 значение первого параметра скрипта
parametr1=$1
#присваиваем переменной script_name значение имени скрипта
script_name=$0
# команда echo выводит определенную строку, обращение к переменным осуществляется через $имя_переменной.
echo "Вы запустили скрипт с именем $script_name и параметром $parametr1"
# здесь мы видим другие кавычки, разница в том, что в одинарных кавычках не происходит подстановки переменных.
echo 'Вы запустили скрипт с именем $script_name и параметром $parametr1'
#Выход с кодом 0 (удачное завершение работы скрипта)
exit 0
Результат выполнения скрипта:
ite@ite-desktop:~$ ./test.sh qwerty
Вы запустили скрипт с именем ./test.sh и параметром qwerty
Вы запустили скрипт с именем $script_name и параметром $parametr1
После того как мы познакомились как использовать переменные и передавать скрипту параметры, время познакомиться с зарезервированными переменными:
$DIRSTACK
— содержимое вершины стека каталогов
$EDITOR
— текстовый редактор по умолчанию
$EUID
— Эффективный UID. Если вы использовали программу su для выполнения команд от другого пользователя, то эта переменная содержит UID этого пользователя, в то время как…
$UID
— …содержит реальный идентификатор, который устанавливается только при логине.
$FUNCNAME
— имя текущей функции в скрипте.
$GROUPS
— массив групп к которым принадлежит текущий пользователь
$HOME
— домашний каталог пользователя
$HOSTNAME
— ваш hostname
$HOSTTYPE
— архитектура машины.
$LC_CTYPE
— внутренняя переменная, котороя определяет кодировку символов
$OLDPWD
— прежний рабочий каталог
$OSTYPE
— тип ОС
$PATH
— путь поиска программ
$PPID
— идентификатор родительского процесса
$SECONDS
— время работы скрипта(в сек.)
$#
— общее количество параметров переданных скрипту
$*
— все аргументы переданыне скрипту(выводятся в строку)
$@
— тоже самое, что и предыдущий, но параметры выводятся в столбик
$!
— PID последнего запущенного в фоне процесса
$$
— PID самого скрипта
Условия
Условные операторы, думаю, знакомы практически каждому, кто хоть раз пытался на чем-то писать программы. В bash условия пишутся след. образом (как обычно на примере):
#!/bin/bash
#в переменную source засовываем первый параметр скрипта
source=$1
#в переменную dest засовываем второй параметр скрипта
dest=$2
# в ковычках указываем имена переменных для сравнения. -eq — логическое сравнение обозначающие «равны»
if [[ "$source" -eq "$dest" ]]
# если они действительно равны, то
then
#выводим сообщение об ошибке, т.к. $source и $dest у нас равны
echo "Применик $dest и источник $source один и тот же файл!"
# выходим с ошибкой (1 — код ошибки)
exit 1
# если же они не равны
else
# то выполняем команду cp: копируем источник в приемник
cp $source $dest
echo "Удачное копирование!"
fi
#обозначаем окончание условия.
Результат выполнения скрипта:
ite@ite-desktop:~$ ./primer2.sh 1 1
Применик 1 и источник 1 один и тот же файл!
ite@ite-desktop:~$ ./primer2.sh 1 2
Удачное копирование!
Структура if-then-else
используется следующим образом:
if
<команда или набор команд возвращающих код возврата(0 или 1)>
then
<если выражение после if истино, то выполняется этот блок>
else
<если выражение после if ложно, тот этот>
В качестве команд возвращающих код возврата могут выступать структуры [[ , [ , test, (( ))
или любая другая(или несколько) linux-команда.
test
— используется для логического сравнения. после выражения, неоьбходима закрывающая скобка «]»
[
— синоним команды test
[[
— расширенная версия «[» (начиная с версии 2.02)(как в примере), внутри которой могут быть использованы || (или), & (и). Долна иметь закрывающуб скобку «]]»
(( ))
— математическое сравнение.
для построения многоярусных условий вида:
if ...
then ....
else
if ....
then....
else ....
для краткости и читаемости кода, можно использовать структуру:
if ..
then ...
elif ...
then ...
elif ...
Условия. Множественный выбор
Если необходимо сравнивать какоую-то одну переменную с большим количеством параметров, то целесообразней использовать оператор case.
#!/bin/bash
echo "Выберите редатор для запуска:"
echo "1 Запуск программы nano"
echo "2 Запуск программы vi"
echo "3 Запуск программы emacs"
echo "4 Выход"
#здесь мы читаем в переменную $doing со стандартного ввода
read doing
case $doing in
1)
/usr/bin/nano
# если $doing содержит 1, то запустить nano
;;
2)
/usr/bin/vi
# если $doing содержит 2, то запустить vi
;;
3)
/usr/bin/emacs
# если $doing содержит 3, то запустить emacs
;;
4)
exit 0
;;
*)
#если введено с клавиатуры то, что в case не описывается, выполнять следующее:
echo "Введено неправильное действие"
esac
#окончание оператора case.
Результат работы:
ite@ite-desktop:~$ ./menu2.sh
Выберите редатор для запуска:
1 Запуск программы nano
2 Запуск программы vi
3 Запуск программы emacs
4 Выход
После выбор цифры и нажатия Enter запуститься тот редактор, который вы выбрали(если конечно все пути указаны правильно, и у вас установлены эти редакторы )
Прведу список логических операторв, которые используются для конструкции if-then-else-fi:
-z
# строка пуста
-n
# строка не пуста
=, (==)
# строки равны
!=
# строки неравны
-eq
# равно
-ne
# неравно
-lt,(< )
# меньше
-le,(<=)
# меньше или равно
-gt,(>)
#больше
-ge,(>=)
#больше или равно
!
#отрицание логического выражения
-a,(&&)
#логическое «И»
-o,(||)
# логическое «ИЛИ»
С основами языка и условиями мы разобрались, чтобы не перегружать статью, разобью её на несколько частей(допустим на 3). Во второй части разберем операторы цикла и выполнение математических операций.
Основы BASH. Часть 2
Циклы. Цикл for-in.
Оператор for-in
предназначен для поочередного обращения к значениям перечисленным в списке. Каждое значение поочередно в списке присваивается переменной.
Синтаксис следующий:
for переменная in список_значений
do
команды
done
Рассмотрим небольшой пример:
#!/bin/bash
for i in 0 1 2 3 4 #переменной $i будем поочередно присваивать значения от 0 до 4 включительно
do
echo "Console number is $i" >> /dev/pts/$i #Пишем в файл /dev/pts/$i(файл виртуального терминала) строку "Console number is $i"
done #цикл окончен
exit 0
После выполнения примера в первых 5 виртуальных консолях(терминалах) появится строка с её номером. В переменную $i
поочередно подставляются значения из списка и в цикле идет работа со значением этой переменной
Циклы. Цикл while.
Цикл while
сложнее цикла for-in
и используется для повторения команд, пока какое-то выражение истинно( код возврата = 0).
Синтаксис оператора следующий:
while выражение или команда возвращающая код возврата
do
команды
done
Пример работы цикла рассмотрим на следующем примере:
#!/bin/bash
again=yes #присваиваем значение "yes" переменной again
while [ "$again" = "yes" ] #Будем выполнять цикл, пока $again будет равно "yes"
do
echo "Please enter a name:"
read name
echo "The name you entered is $name"
echo "Do you wish to continue?"
read again
done
echo "Bye-Bye"
А теперь результат работы скрипта:
ite@ite-desktop:~$ ./bash2_primer1.sh
Please enter a name:
ite
The name you entered is ite
Do you wish to continue?
yes
Please enter a name:
mihail
The name you entered is mihail
Do you wish to continue?
no
Bye-Bye
Как видим цикл выполняется до тех пор, пока мы не введем что-то отличное от «yes». Между do
и done
можно описывать любые структуры, операторы и т.п., все они будут выполнятся в цикле.Но следует быть осторожным с этим циклом, если вы запустите на выполнение в нём какую-либо команду, без изменения переменной выражения, вы можете попасть в бесконечный цикл.
Теперь об условии истинности. После while
, как и в условном операторе if-then-else
можно вставлять любое выражение или команду, которая возвращает код возврата, и цикл будет исполнятся до тех пор, пока код возврата = 0! Оператор [
аналог команды test
, которая проверяет истинность условия, которое ей передали.
Рассмотрим еще один пример, я взял его из книги Advanced Bash Scripting. Уж очень он мне понравился :), но я его немного упростил. В этом примере мы познакомимся с еще одним типом циклов UNTIL-DO. Эта практически полный аналог цикла WHILE-DO, только выполняется пока какое-то выражение ложно.
Вот пример:
#!/bin/bash
echo "Введите числитель: "
read dividend
echo "Введите знаменатель: "
read divisor
dnd=$dividend #мы будем изменять переменные dividend и divisor,
#сохраним их знания в других переменных, т.к. они нам
#понадобятся
dvs=$divisor
remainder=1
until [ "$remainder" -eq 0 ]
do
let "remainder = dividend % divisor"
dividend=$divisor
divisor=$remainder
done
echo "НОД чисел $dnd и $dvs = $dividend"
Результат выполнения скрипта:
ite@ite-desktop:~$ ./bash2_primer3.sh
Введите числитель:
100
Введите знаменатель:
90
НОД чисел 100 и 90 = 10
Математические операции
Команда let
.
Команда let
производит арифметические операции над числами и переменными.
Рассмотрим небольшой пример, в котором мы производим некоторые вычисления над введенными числами:
#!/bin/bash
echo "Введите a: "
read a
echo "Введите b: "
read b
let "c = a + b" #сложение
echo "a+b= $c"
let "c = a / b" #деление
echo "a/b= $c"
let "c <<= 2" #сдвигает c на 2 разряда влево
echo "c после сдвига на 2 разряда: $c"
let "c = a % b" # находит остаток от деления a на b
echo "$a / $b. остаток: $c "
Результат выполнения:
ite@ite-desktop:~$ ./bash2_primer2.sh
Введите a:
123
Введите b:
12
a+b= 135
a/b= 10
c после сдвига на 2 разряда: 40
123 / 12. остаток: 3
Ну вот, как видите ничего сложного, список математических операций стандартный:
+
— сложение
—
— вычитание
*
— умножение
/
— деление
**
— возведение в степень
%
— модуль(деление по модулю), остаток от деления
let
позволяет использовать сокращения арифметических команд, тем самым сокращая кол-во используемых переменных. Например:a = a+b
эквивалентноa +=b
и т.д
Работа с внешними программами при написании shell-скриптов
Для начала немного полезной теории.
Перенаправление потоков.
В bash (как и многих других оболочках) есть встроенные файловые дескрипторы: 0 (stdin)
, 1 (stdout)
, 2 (stderr)
.
stdout
— Стандартный вывод. Сюда попадает все что выводят программы
stdin
— Стандартный ввод. Это все что набирает юзер в консоли
stderr
— Стандартный вывод ошибок.
Для операций с этими дескрипторами, существуют специальные символы: >
(перенаправление вывода), <
(перенаправление ввода). Оперировать ими не сложно. Например:
cat /dev/random > /dev/null #перенаправить вывод команды cat /dev/random в /dev/null (абсолютно бесполезная операция :)) )
или
ls -la > listing #записать в файл listing содержание текущего каталога (уже полезней)
Если есть необходимость дописывать в файл(при использовании «>
» он заменятеся), необходимо вместо «>
» использовать «>>
«
sudo < my_password
после просьбы sudo ввести пароль, он возьмется из файла my_password, как будто вы его ввели с клавиатуры.
Если необходимо записать в файл только ошибки, которые могли возникнуть при работе программы, то можно использовать:
./program_with_error 2> error_file
цифра 2 перед «>
» означает что нужно перенаправлять все что попадет в дескриптор 2(stderr).
Если необходимо заставить stderr
писать в stdout
, то это можно след. образом:
./program_with_error 2>&1
символ «&
» означает указатель на дескриптор 1(stdout)
(Поумолчанию stderr
пишет на ту консоль, в котрой работает пользователь(вренее пишет на дисплей)).
2. Конвееры.
Конвеер — очень мощный инструмент для работы с консолью Bash. Синтаксис простой:
команда1 | команда 2
— означает, что вывод команды 1 передастся на ввод команде 2
Конвееры можно группировать в цепочки и выводить с помощью перенаправления в файл, например:
ls -la | grep «hash» |sort > sortilg_list
вывод команды ls -la
передается команде grep
, которая отбирает все строки, в которых встретится слово hash, и передает команде сортировке sort
, которая пишет результат в файл sorting_list. Все довольно понятно и просто.
Чаще всего скрипты на Bash используются в качестве автоматизации каких-то рутинных операций в консоли, отсюда иногда возникает необходимость в обработке stdout
одной команды и передача на stdin
другой команде, при этом результат выполнения одной команды должен быть неким образом обработан. В этом разделе я постораюсь объяснить основные принципы работы с внешними командами внутри скрипта. Думаю что примеров я привел достаточно и можно теперь писать только основные моменты.
1. Передача вывода в переменную.
Для того чтобы записать в переменную вывод какой-либо команды, достаточно заключить команду в ``
ковычки, например
a = ` echo «qwerty» `
echo $a
Результат работы: qwerty
Однако если вы захотите записать в переменную список директорий, то необходимо, должным образом обработать результат для помещения данных в переменную. Рассмотрим небольшой, пример:
LIST=`find /svn/ -type d 2>/dev/null| awk '{FS="/"} {print $4}'| sort|uniq | tr 'n' ' '`
for ONE_OF_LIST in $LIST
do
svnadmin hotcopy /svn/$ONE_OF_LIST /svn/temp4backup/$ONE_OF_LIST
done
Здесь мы используем цикл for-do-done
для архивирование всех директорий в папке /svn/ с помощью команды svnadmin hotcopy
(что в нашем случае не имеет никого значения, просто как пример). Наибольшй интерес вызывает строка: LIST=find /svn/ -type d 2>/dev/null| awk '{FS="/"} {print $4}'| sort|uniq | tr 'n' ' '
В ней переменной LIST присваивается выполнение команды find, обработанной командами awk, sort, uniq,tr(все эти команды мы рассматривать не будем, ибо это отдельная статья). В переменной LIST будут имена всех каталогов в папке /svn/ пгомещенных в одну строку(для того чтобы её стравить циклу.
Написание скриптов в Windows 10 Bash
Справка:https://www.howtoip.com/how-to-create-and-run-bash-shell-scripts-on-windows-10/
Пример тестировался мной в подсистеме linux windows pro build 15063
Как писать скрипты на win 10 bash
Вы можете писать сценарии в системе Windows, но вам нужно выполнить преобразование EOL> формат UNIX / OSX, чтобы дать символ строки UNIX конца файла
(Это можно сделать в некоторых программных блокнотах, таких как блокнот ++)
В среде Bash на основе Ubuntu с двумяVIс участиемНано-нано
НаноИспользуйте: nano ~ / myscript.sh — командный файл (файл использует абсолютный путь в bash)
~ — Представляет ваш домашний каталог, поэтому полный путь — /home/username/myscript.sh)
Описание скрипта: добавить#!/bin/bash
Весь процесс скрипта от написания до исполнения
Случай:
#!/bin/bash
read -p “Please input yes or no:” anw
case $anw in
[yY][eE][sS]|[yY])
echo yes
;;
[nN][oO]|[nN])
echo no
;;
*)
echo false
;;
esac
Оцените ввод да или нет
- Выполните подсистему windows 10 linux: напрямую введите bash.exe в командную строку или запустите и нажмите Enter.
- Запустите редактор файлов: nano yesorno-new.sh
- После ввода содержимого нажмите ctrl + o, а затем Enter, чтобы сохранить. Снова нажмите Ctrl + X
-
Сделайте скрипт исполняемым и запустите его: Измените права доступа: chmod + x ~ / yesorno.sh Выполните: ~ / yesorno.sh
График эффекта бега:
Как использовать файлы Windows в сценарии Bash
Чтобы получить доступ к файлам Windows в скрипте, вам необходимо указать их путь в / mnt / c, а не путь к ним.
Например
Путь в Windows: C: users bob downloads test.txt
Путь в Bash: /mnt/c/users/bob/downloadstest.txt
Как включить команды Bash в пакетные сценарии или сценарии PowerShell
Запустите команды Linux из PowerShell:
bash -c «команда»
Are you interested in working with shell scripts on Windows? Thanks to a recent addition by Microsoft, you can now use Windows Subsystem for Linux to make this happen.
Once you enable shell scripts in Windows 10, you can start creating shell scripts of your own. Shell scripts are great for automating simple tasks. You can also work on open-source Linux-based projects if that’s an area of interest to you. Finally, you’ll learn how to execute shell scripts on Windows 10. Continue reading to learn more.
What is Linux?
Linux is an open-source operating system that is highly popular among computer enthusiasts. Linux competes with other computer operating systems like Microsoft’s Windows, Apple’s macOS, and mobile operating systems like Android and iOS.
The Linux operating system was originally developed by Linus Torvalds back in 1991. The Linux kernel was designed as a Unix-like operating system. Unix was an early operating system created by Bell Labs in 1969. Today, modern Linux distributions are still Unix-like, meaning they retain the basic structure and properties that Unix had. An example of a non-Unix operating system would be Microsoft Windows.
The top Linux distributions have changed over the years, but as of 2022, Ubuntu, Debian, CentOS, Fedora, and Red Hat rank as the top 5 most popular options.
What is Bash?
When Linus Torvalds created Linux, he included a Unix shell called Bash. Bash had been created just two years before, in 1989 by Brian Fox. Bash has been the longtime default for Linux and was also the default for Apple macOS until it was replaced by Z shell in 2019.
Until 2016, Windows users could not use the Linux kernel or Bash at all. Windows first introduced the Windows Subsystem for Linux (WSL) beta with Windows 10 version 1607 update. About a year later, in October 2017, WSL was fully released in Windows 10 version 1709. Microsoft developed WSL for hobbyists and developers who want to work on open-source Linux-based projects.
It’s important to note that WSL is not preinstalled on Windows 10. If you would like access to create and run shell scripts on Windows 10, you will need to manually install WSL or join the Windows insider program.
What is a shell script?
A Shell script is a type of script that cannot be run without a Unix shell. Further, a shell script is a series of commands that are executed line by line by the command line.
You can use shell scripts to automate processes and avoid repetitive tasks. Instead of manually completing each step in a series, you can execute a script, and the command line will handle the rest.
For example, if you find yourself regularly stopping processes that are hogging your CPU, you can automate this process with a script. When you execute the script, it may be designed to find a set of processes using CPU resources and request to kill them.
Enabling shell scripts in Windows 10
- Click on the Start (Windows) button and enter “Control Panel” into the search bar. Click Open on the Control Panel result on the right-hand side.
- Within the Control Panel window, find and click on Programs.
- Now, from the Programs window, find Click Turn Windows features on or off underneath the Programs and Features header.
- In the Windows Features window, scroll to the very bottom of the window. Check the Windows Subsystem for Linux option. Then click OK.
- Windows will automatically install the necessary files. When the installation is complete, select Restart Now.
- When your computer restarts, you need to install Ubuntu from the Microsoft store.
- After installation, make sure you open Ubuntu and see it up. You are now ready to use scripts on your Windows 10 machine.
If you encounter any issues with Ubuntu or bash commands not working correctly, you may want to check that Virtualization is turned on in your BIOS. The most updated WSL version, WSL 2, runs the Linux kernel using virtualization technology. This means a virtual machine needs to be able to run on your system.
Now that Windows Subsystem for Linux and Ubuntu has been installed, you are ready to start creating shell scripts in Windows 10. You may be tempted to write bash scripts with Notepad, but this is not recommended. Because Notepad is designed for Windows/DOS systems, the line endings will differ from those that are found at the end of Unix/Linux line endings.
Text editors for shell scripts
You should use software that is designed to convert to Unix/OSX end-of-line characters. The best open-source software available for this is Notepad++. Amazingly, Notepad++ is lovingly maintained and developed by a single individual, Don Ho.
If you try Notepad++ and don’t like it, you can try another fan favorite, nano. Nano is a text editor for Unix/Linux systems. You can easily create shell scripts that will run in bash, using nano. Download nano to get started.
Example shell scripts
Let’s look at some basic shell scripts, so you can learn more about what you are going to be coding and see how some formatting and syntax work.
1. Hello World!
echo "Hello World!"
This script will print out the infamous Hello World! Notice that echo can be used as a print command when not combined with any other modifiers. It will print the string on a new line. If you add the -n modifier, the output will print on the same line.
2. Sum two numbers
If you want to do some basic arithmetic, you might have a script that looks like:
# Add two numbers together
((sum=25+35))
# Print the sum of the numbers
echo $sum
Note that the # symbol is used to make comments that are not expressed. The output of this script will print the sum of 25+35, which is 60.
3. Take user input
The following script will ask for the user’s name and then use the read command to take the user’s input. Then the user’s name is passed into the following expression, ultimately welcoming you to Windows Subsystem for Linux.
echo "What is your name?"
read name
echo "Welcome $name to Windows Subsystem for Linux."
Write basic shell scripts in Windows 10
Continue reading to learn how to write basic shell scripts in Windows 10 using Notepad++.
- Click the Start button and search for “Notepad++” and click Run as administrator on the right-hand side.
- Now you can create your script.
- Once your script is complete, you need to use the EOL Conversion option available in Notepad++. Click Edit and locate EOL Conversion from the dropdown menu. Hover over this option and then select UNIX/OSX Format from the next dropdown menu.
- Now select File and then Save As. Make sure to name your file something you will recognize and add .sh to make it a shell script file.
- Once the shell script is saved, continue to the next section to learn how to run your own shell scripts.
How to run shell scripts (.sh files) on Windows 10
You’ve created your first shell scripts, and it’s time to execute the sh file. Remember that when using WSL, you can only use Linux commands and utilities. Windows 10 programs will not work in bash scripts. To execute a script file, follow these step-by-step instructions:
- Click on the Start (Windows) button and enter “Command Prompt” into the search bar. Click Run as administrator on the Command Prompt result on the right-hand side.
- Navigate to the folder where the script file is saved. You move around in the command prompt using the cd command. For example, if you want to access the Documents folder, you would enter the following and press Enter:
cd C:UsersUsernameOneDriveDocuments
Note: Username would be the username that you set up for yourself when you registered your computer.
- Now enter bash file-name.sh, where file-name is the whatever you’ve named your script file.
bash file-name.sh
The script will execute, and if there are any outputs or print statements included in the script, the output will be returned.
Bash scripts running on Windows 10
You’ve made it far and learned a ton of information in one go. Command-line utilities, different operating systems, and learning to write and execute shell scripts can be difficult topics. In fact, these topics will take time to master. You have a ton of learning to do for scripting, but resources are available to help you all over the internet.
Within this guide, you learned the basics of Linux and Bash. You learned what shell scripts are and that you need to specifically enable Windows Subsystem for Linux (WSL) to use them. You learned how to create shell scripts using Notepad++ and how to execute the scripts in bash. Enjoy experimenting!
С появлением оболочки Bash Windows 10 вы можете теперь создавать и запускать сценарии оболочки Bash в Windows 10. Вы также можете включить команды Bash в командный файл Windows или сценарий PowerShell.
Даже если вы знаете, что делаете, это не обязательно так просто, как кажется. Windows и UNIX используют разные символы конца строки, а файловая система Windows доступна в другом месте в среде Bash.
Как написать сценарий Bash в Windows 10
При написании сценариев оболочки в Windows помните, что Windows и UNIX-подобные системы, такие как Linux, используют разные символы «конца строки» в текстовых файлах в сценариях оболочки.
Другими словами, это означает, что вы не можете просто написать сценарий оболочки в «Блокноте». Сохраните файл в «Блокноте», и он не будет правильно интерпретироваться Bash. Тем не менее, вы можете использовать более сложные текстовые редакторы, например, Notepad ++ позволяет вам предоставить файл конечных символов UNIX, нажав «Редактировать»> «Преобразование EOL»> «Формат UNIX / OSX».
Однако вам лучше просто написать сценарий оболочки в самой среде Bash. В среде Bash, основанной на Ubuntu, есть как редакторы vi, так и nano. Редактор vi более мощный, но если вы никогда не использовали его раньше, вы можете начать с nano. Это проще в использовании, если вы новичок.
Например, чтобы создать скрипт bash в nano, вы выполните следующую команду в bash:
nano ~/myscript.sh
Это откроет текстовый редактор Nano, указанный в файле с именем «myscript.sh» в домашнем каталоге вашей учетной записи пользователя. (Символ «~» представляет ваш домашний каталог, поэтому полный путь: /home/username/myscript.sh.)
Запустите сценарий оболочки с помощью строки:
#!/bin/bash
Введите команды, которые вы хотите запустить, каждый из которых находится в отдельной строке. Скрипт будет запускать каждую команду по очереди. Добавьте символ «#» перед строкой, чтобы рассматривать его как «комментарий», что поможет вам и другим людям понять сценарий, но который не запускается как команда. Для получения более совершенных трюков обратитесь к более подробному руководству по сценариям Bash в Linux. Те же методы будут работать в Bash на Ubuntu в Windows.
Обратите внимание: нет возможности запуска программ Windows из среды Bash. Вы ограничены командами и утилитами терминала Linux, так же, как и в обычной Linux-системе.
Например, давайте просто воспользуемся базовым сценарием «hello world» в качестве примера:
#!/bin/bash # set the STRING variable STRING='Hello World!' # print the contents of the variable on screen echo $STRING
Если вы используете текстовый редактор Nano, вы можете сохранить файл, нажав Ctrl + O, а затем Enter. Закройте редактор, нажав Ctrl + X.
Сделайте исполняемый файл сценария, а затем запустите его
Вероятно, вам понадобится сделать исполняемый файл сценария, чтобы вы могли легко запускать его. В Linux это означает, что вам нужно предоставить файлу сценария исполняемое разрешение. Для этого запустите следующую команду в терминале, указав ее на свой скрипт:
chmod +x ~/myscript.sh
Чтобы запустить скрипт, вы можете просто запустить его в терминале, введя его путь. Всякий раз, когда вы хотите запустить сценарий в будущем, просто откройте оболочку Bash и введите путь к скрипту.
~/myscript.sh
(Если скрипт находится в текущем каталоге, вы можете запустить его с ./myscript.sh)
Как работать с файлами Windows в сценарии Bash
Чтобы получить доступ к файлам Windows в сценарии, вам нужно указать их путь в / mnt / c, а не в пути к ним. Например, если вы хотите указать файл C: Users Bob Downloads test.txt, вам нужно указать путь /mnt/c/Users/Bob/Downloads/test.txt. Для получения дополнительной информации обратитесь к нашему руководству по расположению файлов в оболочке Bash Windows 10.
Как включить команды Bash в скрипт Batch или PowerShell
Наконец, если у вас есть существующий командный файл или сценарий PowerShell, в который вы хотите включить команды, вы можете запускать команды Bash напрямую, используя bash -c
команда.
Например, чтобы запустить команду Linux в окне командной строки или PowerShell, вы можете запустить следующую команду:
bash -c 'command'
Этот трюк позволяет добавлять команды Bash в командные файлы или сценарии PowerShell. Окно оболочки Bash появится, когда запущена команда Bash.
Обновить: Если у вас установлено несколько Linux-сред, вы можете использовать команду wslconfig для выбора среды Linux по умолчанию, используемой при запуске bash -c
команда.
Чтобы создать ярлык для сценария Bash изнутри Windows, просто создайте ярлык, как обычно. Для цели ярлыка используйте bash -c
которую мы описали выше, и укажем на созданный вами сценарий Bash.
Например, вы укажете ярлык на » bash -c '~/myscript.sh'
Msgstr «запустить приведенный выше пример скрипта. Вы также можете просто запустить эту команду из окна командной строки или окна PowerShell.
Tweet
Share
Link
Plus
Send
Send
Pin
Bash, он же возрождённый shell, является по-прежнему одним из самых популярных командных процессоров и интерпретаторов сценариев. Как бы его ненавидели и не пытались заменить, всё равно он присутствует вокруг нас и никуда не собирается исчезать. Если вам приходится писать bash скрипты или вы только планируете этим заняться, данная статья написана для вас.
Статья несет исключительно рекомендательный характер и затрагивает в первую очередь bash, но также будет полезна и для работы с совместимыми оболочками, такими как: sh, ash, csh, ksh и tcsh.
На данный момент, тема bash скриптинга не менее актуальна чем 10 или 20 лет назад. Хотя большинство дистрибутивов Linux перешли на Systemd или аналоги, а System V с скриптами для запуска служб ушел на пенсию, многое по прежнему реализовано при помощи скриптов и многие из них — это shell скрипты. В ключевых и популярных дистрибутивах, bash является оболочкой по умолчанию, и это неспроста, при помощи него легко автоматизировать рутину. С появлением docker, а после и kubernetes, тема bash скриптов стала только актуальнее, количество всевозможных docker-entrypoint.sh
, Job, CronJob и initContainers растет, а реализуются они чаще всего при помощи bash. Инструкция RUN
в Dockerfile
, вовсе внесла огромный вклад в мировой запас shell строк.
Это несложно, но необходимо знать некоторые основы bash скриптинга. Поехали!
Текстовый редактор
Начнем с выбора среды разработки, именно она позволяет объединять различные аспекты написания программы, повышая продуктивность за счет объединения общих действий по написанию программного обеспечения в одном приложении.
Я в своей практике использовал разные редакторы для работы с shell скриптами, приведенный список не является конечным, и перечислю только те, которые запомнились больше всего. Разделим на три условные категории:
-
Консольные текстовые редакторы. Vim, Emacs и Nano — классическая троица, сейчас уже редко кто использует на рабочих станциях как основной инструмент, но vi и nano незаменимы для быстрого редактирования файлов в удаленных ssh сессиях. Если вы еще не работали с ним, рекомендую освоить такие вещи как поиск, замена и форматирование, хотя бы в nano.
-
Графические текстовые редакторы. Mousepad, Gedit, Notepad++ и т.п. Легковесные редакторы, с подсветкой синтаксиса, автозаменой и прочим, что уже есть в консольных редакторах, но они всё еще не являются полноценной интегрированной средой разработки.
-
IDE. Geany, Atom, IntelliJ IDEA, Sublime Text и Visual Studio Code — это уже полноценные и расширяемые среды разработки. Долгие годы я пользовался Geany и пробовал все перечисленные варианты, но только с появлением VSCode мне удалось сменить основную IDE для большинства задач.
ℹ️ Половина статьи затрагивает конфигурацию параметров и расширения для Visual Studio Code. Хотя, эта IDE может быть не в вашем вкусе, но информация приведенная в статье будет полезна в академических целях, а полученные знания, вы можете адаптировать под свое любимое окружение.
Альтернативные редакторы
Существует как минимум три альтернативных среды разработки для написания bash скриптов:
-
Специализированная IDE BashEclipse основанная на Eclipse.
-
В IntelliJ IDEA можно добиться расширенной поддержки bash скриптинга путем установки расширений Shell Script, ShellCheck и BashSupport.
-
Bash Kernel для Jupyter Notebook.
Настройка окружения
Сперва следует настроить редактор так, чтобы он помогал нам писать скрипты в едином стиле и исправлял за нас небольшие огрехи.
Ширина строк кода
Начнем мы с того, что выведем на экран вертикальные линии для отображения столбцов 72, 80 и 132 символов, данная длина является «стандартным» пределом для ширины кода. И мы будем стараться при написании скриптов умещаться в границу 80 столбцов, 72 столбец будет нашим желанным ориентиром, а 132 столбец пометим как край документа, применимый в исключительных ситуациях, за ним же следует пропасть.
Добавим нижеприведенную конфигурацию в файл настроек settings.json
для Visual Studio Code.
Как найти settings.json
-
Откройте Visual Studio Code
-
Нажмите F1, чтобы открыть командную панель
-
Введите в открывшуюся панель «open settings»
-
Вам представлены два варианта, выберите «Open Settings (JSON)»
{
"[shellscript]": { // настройки применимые только для shellscript файлов
"editor.rulers": [ // вертикальные лини подсветки столбцов 72, 80, 132
{ "column": 72, "color": "#1e751633", },
{ "column": 80, "color": "#c2790b99", },
{ "column": 132, "color": "#a10d2d99" }
],
"editor.minimap.maxColumn": 132, // ширина миникарты
"editor.wordWrap": "off", // запрещаем перенос строк
},
// ... прочие настройки
}
Краткий ответ, почему именно 72, 80 и 132 символа
Вы можете поблагодарить перфокарту IBM 1928 года за этот предел — в ней было 80 столбцов. Почему 80? Дело в том, что типичный шаг пишущей машинки 10-12 символов на дюйм, а это приводит к документам шириной от 72 до 90 символов, в зависимости от размера полей. После этого ранние телетайпы, а затем видео-терминалы использовали 80 столбцов, а затем 132 столбца в качестве стандартной ширины. Сейчас же, к примеру, пропорции окна эмулятора терминала по умолчанию остаются равными 80×24.
Но ведь сейчас же не 1928 год! Да, но эргономика чтения файлов в 80 столбцов гораздо выше, а при сравнении двух файлов мы используем в два раза большую ширину (бывают еще diff-ы трех состояний). Все еще используются удаленные параллельные терминалы в гипервизорах и KVM-свитчи в серверных, и порой приходится что-то быстро поправить через них, находясь в сессии с разрешением 1024×768, а может и меньшим.
Возможно, вы владелец 4K+ дисплея и думаете, что вас это не касается. Но подумайте о других, кто будет использовать ваши скрипты, если конечно они публикуются за рамками вашего localhost.
Отступы и окончание строк
Одной из проблем, которую я встретил 15 лет назад, когда только знакомился с bash и наблюдаю по сей день, это CRLF (rn
или 0x0D0A
) в файлах сценариев. Источником проблемы, чаще всего является копипаста bash скриптов в windows системах, но также это может быть и просто по невнимательности. Давайте настроим завершение сток при помощи LF.
Помимо типа окончания строк, также включим добавление пустой строки в конец файла и настроим отступы и их визуализацию.
{
"[shellscript]": { // настройки применимые только для shellscript
"files.eol": "n", // явно зададим LF формат EOL
"files.insertFinalNewline": true, // завершаем все файлы новой строкой
"files.trimFinalNewlines": true, // удалим лишние новые строки в конце файла
"files.trimTrailingWhitespace": true, // удалим лишние пробелы в конце строк
"editor.renderWhitespace": "boundary", // отобразим два и более пробелов
"editor.insertSpaces": true, // отступы делаем пробелами
"editor.tabSize": 2, // размер отступа в два пробела
},
// ... прочие настройки
}
Чем страшен CRLF?
Возврат каретки — это управляющий символ. Когда вы печатаете его на терминале, вместо отображения глифа терминал выполняет некоторый специальный эффект. Для возврата каретки специальный эффект заключается в перемещении курсора в начало текущей строки. Таким образом, если вы напечатаете строку, содержащую возврат каретки посередине, то в результате вторая половина будет записана поверх первой.
Давайте рассмотрим на примере, у нас есть простейший скрипт:
#!/usr/bin/env bash
set -eu
printf '%s ' "Hi ${USER:-John Doe}! Today is"
LANG=en date
Сохраним его в файл test-eol.sh
, сделаем его исполняемым chmod +x ./test-eol.sh
и проверим работу:
# ./test-eol.sh
Hi woozymasta! Today is Mon Oct 18 00:48:13 MSK 2021
Всё хорошо, давайте заменим LF на CRLF, можно воспользоваться командой sed $'s/$/r/' -i test-eol.sh
и запустим сценарий еще раз:
# ./test-eol.sh
/usr/bin/env: 'bashr': No such file or directory
Скрипт упал с ошибкой о том, что файла bashr
не существует, утилита env приняла на вход строку как есть. И это хорошо, что скрипт упал, ведь могло произойти что-то более непредвиденное. Давайте обойдем использование shebang, передав путь к скрипту как аргумент для bash:
# bash ./test-eol.sh
./bash-eol.sh: line 2: $'r': command not found
: invalid optionine 3: set: -
set: usage: set [-abefhkmnptuvxBCHP] [-o option-name] [--] [arg ...]
./bash-eol.sh: line 4: $'r': command not found
./bash-eol.sh: line 6: $'dater': command not found
Как видно, теперь были обработаны все инструкции скрипта. Правда set -e не смог выполниться, и каждая команда сценария, смогла выполниться с ненулевым кодом возврата.
В этом примере, ничего страшного не произошло, но, что если у вас был бы такой скрипт:
#!/usr/bin/env bash
set -eu
printf '%s ' "Hi ${USER:-John Doe}! Today is"
LANG=en date ||
{ rm -rf / --no-preserve-root; echo "you will not pass"; }; echo Done
❗ Осторожно! Для проверки этого скрипта, всё же лучше замените
rm -rf / --no-preserve-root
, к примеру наtouch test
Зачем нужна пустая строка в конце файла?
Речь идет не о добавлении дополнительной строки в конец файла, а о том, чтобы не удалять новую строку, которая должна быть там.
Текстовый файл в Unix состоит из серии строк, каждая из которых заканчивается символом новой строки n
. Таким образом, файл, который не является пустым и не заканчивается новой строкой, не является текстовым файлом.
Утилиты, которые должны работать с текстовыми файлами, могут не справиться с файлами, которые не заканчиваются символом новой строки. Исторические утилиты Unix могут, например, игнорировать текст после последней новой строки. Утилиты GNU придерживаются политики приличного поведения с нетекстовыми файлами, как и большинство других современных утилит, но вы все равно можете столкнуться со странным поведением с файлами, в которых отсутствует последняя новая строка.
И я бы предложил использовать добавление новой строки по умолчанию, во все редактируемые файлы, естественно если на то нет ограничений у формата.
Автосохранение
Данный момент выделен отдельно неспроста, редактировать shell сценарии в процессе их работы крайне нежелательно. Всё потому, что файл читается построчно и внесенные правки во время работы сценария могут вызвать непредвиденное поведение.
Просто помните об этом, и не включайте автосохранение при написании bash скриптов. К сожалению параметр files.autoSave
не поддерживается для выборочных типов файлов, а устанавливается глобально на всё окружение.
Проверим на практике редактирование уже исполняющегося скрипта
Ситуации могут быть разные, к примеру, у вас есть долгоиграющий скрипт, и вы, в процессе его работы решили, всего лишь добавить еще одно отладочное сообщение в теле цикла. Скорее всего вас ждут проблемы на выходе из цикла.
А теперь рассмотрим простой доказательный пример, создадим скрипт test.sh
работающий две секунды:
#!/usr/bin/env bash
sleep 1s
echo one
sleep 1s
echo two
Запустим скрипт и по итогу выполнения напечатаем код выхода, объединим это в группу и запустим в отдельном потоке, а пока он отработает половину отведенного ему времени, допишем в него еще одну команду exit 42 :
{ ./test.sh && echo $?; } & sleep 1s; echo 'exit 42' >> ./test.sh; wait
ℹ️ Если однострочники у вас вызывают некоторое волнение, воспользуйтесь сервисом explainshell.com, он поможет на первых порах разбирать такие конструкции.
И вот итог:
[1] 1208831
one
two
[1]+ Выход 42 { ./test.sh && echo $?; }
Но допустим автосохранение отключать нельзя, или вы сами на автомате нажали Ctrl+S, можно как-то предостеречь это поведение?
Вариантов на самом деле много, начиная созданием копий файла, но я бы предложил отправить скрипт в трубу:
cat ./test.sh | bash -s - "${args[@]}"
Но и здесь имеется ограничение, это размер буфера, равный 65536 байтам, с скриптом вес которого превышает размер буфера, этот трюк уже не пройдет как ожидалось.
Пожалуй это все параметры для редактора, которые хотелось осветить. Для удобства настройки, все параметры которые были внесли в settings.json приведены ниже:
Все параметры в settings.json для shellscript
{
"files.autoSave": "off",
"[shellscript]": {
"files.eol": "n",
"files.insertFinalNewline": true,
"files.trimFinalNewlines": true,
"files.trimTrailingWhitespace": true,
"editor.renderWhitespace": "boundary",
"editor.insertSpaces": true,
"editor.tabSize": 2,
"editor.tabCompletion": "on",
"editor.wordWrap": "off",
"editor.rulers": [
{
"column": 72,
"color": "#1e751633",
},
{
"column": 80,
"color": "#c2790b99",
},
{
"column": 132,
"color": "#a10d2d99"
}
],
"editor.minimap.maxColumn": 132,
}
}
Утилиты и расширения
Всё обилие возможностей и расширенное погружение в написание bash сценариев, открывается при использовании дополнительных утилит, таких как: линтер, отладчик, форматер, языковой сервер и т.п. Сами по себе утилиты хоть и решают свои функциональные задачи, только с интеграцией в IDE они по настоящему раскрывают свою мощь.
ShellCheck
ShellCheck — это инструмент который дает предупреждения и предложения для сценариев bash и sh. Незаменимая вещь, которую следует использовать повсеместно для написания скриптов и встраивать в CI пайплайны. Поможет писать сценарии более корректно и надежно, укажет на типичные проблемы синтаксиса и семантические проблемы, а также уведомит о тонкостях и возможных подводных камнях в разных конструкциях.
Рекомендуется использовать последний релиз приложения.
Для проверки сценария достаточно выполнить:
shellcheck /path/to/script.sh
Пример результата работы shellcheck
In /path/to/script.sh line 5:
echo $none
^---^ SC2154: none is referenced but not assigned.
^---^ SC2086: Double quote to prevent globbing and word splitting.
Did you mean:
echo "$none"
In /path/to/script.sh line 6:
. ./test
^----^ SC1091: Not following: ./test was not specified as input (see shellcheck -x).
Но гораздо нагляднее, будет видеть все предупреждения и подсказки в самой IDE. Для этого установим расширение ShellCheck:
ext install timonwong.shellcheck
BASH Debugger
BASH Debugger — это внешний отладчик для bash, который следует синтаксису команды gdb.
К сожалению в большинстве дистрибутивов пакет или отсутствует, или имеет очень старую версию, по этому соберем проект из исходников. Скачаем последнюю версию или клонируем с зеркала на github и собираем:
tar xf bashdb-5.0-1.1.2.tar.gz
cd bashdb-5.0-1.1.2/
./configure
make
sudo make install
# можно взять один бинарь и обойтись без make install
# если работать c bashdb будем только из vscode
# cp bashdb ~/.local/bin/
Теперь для запуска отладки скрипта выполним команду:
bash --debugger -- /path/to/script.sh
# или
bashdb /path/to/script.sh
Пример результата работы bashdb
bash debugger, bashdb, release 5.0-1.1.2
Copyright 2002-2004, 2006-2012, 2014, 2016-2019 Rocky Bernstein
This is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
(/path/to/script.sh:5):
5: echo $none
bashdb<0> backtrace
->0 in file `./bash-eol.sh' at line 5
##1 main("/usr/share/bashdb/bashdb-main.inc") called from file `/path/to/script.sh' at line 0
bashdb<1> debug
Debugging new script with /usr/bin/bash --init-file /tmp/bashdb_profile_1067091 --debugger echo
/usr/bin/echo: /usr/bin/echo: не удалось запустить двоичный файл
bashdb<2> continue
/path/to/script.sh: строка 6: ./test: Нет такого файла или каталога
(standard_in) 1: syntax error
hi
Done
Но это не так удобно, как расставлять точки останова в IDE, по этому установим расширение Bash-debug:
⚠️ Внимание! Для работы требует наличия bashdb в системе
ext install rogalmic.bash-debug
Shell Format
Shfmt — утилита для форматирования shell сценариев. Установим последний релиз и попробуем на практике:
# Форматировать скрипт с настройками по умолчанию, вывод направить файл
shfmt /path/to/script.sh > /path/to/script-formated.sh
# Перезаписать файл, использовать отступ в два пробела
shfmt -w -i 2 ~/desktop/bash-eol.sh
Очередь расширения, установим Shell Format:
ext install foxundermoon.shell-format
Нам стало доступно форматирование файлов shellscript
, и им уже можно воспользоваться. Для этого вызовем палитру команд, нажав F1, введем в поле «format document» и выберем этот же пункт.
Помимо этого, вы можете настроить автоматическое форматирование документов, при сохранении файла, параметр editor.formatOnSave
.
ℹ️ Увы расширение не позволяет форматировать выделенный блок кода, в связи с этим недоступен параметр
editor.formatOnPaste
, позволяющий форматировать код вставляемый из буфера обмена.
Bash Language Server
Bash Language Server — языковой сервер для интеграции в множество различных IDE. Установка языкового сервера приносит нам поведение среды разработки, как у больших языков программирования, такие возможности как: поиск ссылок, переход к объявлению, автодополнение, документация и т.п.
Для VSCode достаточно установить расширение Bash IDE:
ext install mads-hartmann.bash-ide-vscode
ℹ️ Расширение также поддерживает интеграцию с explainshell, но для этого вам понадобится держать запущенным сервер explainshell, а на выходе вы не получите всей той магии, что доступна на сайте explainshell.com. В связи с этим интеграцию считаю сомнительной, и у себя не использую.
Shell Completion
Работая с bash как оболочкой, во многих моментах помогает автодополнение по TAB, так вот для VSCode есть возможность дополнять аргументы для команд, реализуется это при помощи расширения Shell Completion. Давайте проверим:
ext install tetradresearch.vscode-h2o
Manpages
Самая актуальные и корректные руководства к утилитам, зачастую находится локально в man, почему бы не читать их напрямую в среде разработки. Manpages поможет нам в этом, установим его:
ext install meronz.manpages
Использовать расширение просто, выделяем в теле скрипта имя интересной нам команды и просим показать man через палитру команд или в контекстном меню.
ShellMan
Shellman — наверное единственная совместимая с ShellCheck коллекция сниппетов для bash. Будет полезно как новичкам, для более быстрого знакомства с скриптами, так и бывалым разработчикам позволит сэкономить время на написание рутинных конструкций. В магазине расширений доступно около десятка расширений с снипетами для shell скриптов наряду с Shellman, при желании вы можете комбинировать их.
Установка расширения:
ext install Remisa.shellman
Подробно ознакомится с возможностями и советами как пользоваться ShellMan вы можете в книге shellman-ebook.
Code Runner
Code Runner — расширение, позволяющее выполнять произвольный блок кода в самой IDE, для этого достаточно выделить необходимые строки и нажать CTRL+ALT+N, или вызвать данную функцию из контекстного меню, или палитры команд. Это заметно ускорит процесс написания скриптов.
ext install formulahendry.code-runner
Демонстрация работы (GIF 5МБ)
Hadolint
Hadolint — это, пожалуй лучший линтер для Dockerfile
. Почему он оказался в этом списке? Всё довольно просто, в Dockerfile
имеется инструкция RUN
в которой размещается shell скрипт, а Hadolint помимо общей проверки синтаксиса файла, также использует ShellCheck для проверки этих скриптов.
Скачаем последнюю версию приложения с страницы релизов. Запустим утилиту, передав путь к Dockerfile
как аргумент.
hadolint ./Dockerfile
И установим расширение Hadolint в VSCode:
⚠️ Внимание! Для работы требует наличия hadolint в системе
ext install exiasr.hadolint
И как бонус, для подсветки синтаксиса shell скриптов в RUN секции Dockerfile, можно воспользоваться расширением Better Dockerfile Syntax.
Txt Syntax
Еще одно вспомогательное расширение Txt Syntax, напрямую не влияющее на bash скрипты, но позволяет выделить текстовые файлы (.txt, .out .tmp, .log, .ini, .cfg …) и предоставить общие служебные инструменты для текстовых документов. Shell сценарии часто опираются на всевозможные текстовые файлы, и будет полезно упростить работу с ними в IDE.
ext install xshrim.txt-syntax
ℹ️ Данное расширение помогает работать расширению manpages, а именно складывать и раскладывать заголовки в документах справки.
Better Shell Syntax
И в завершении списка, расширим подсветку синтаксиса. По умолчанию подсветка не настолько хороша как могла быть, и расширение Better Shell Syntax пытается исправить это, позволяя вашей теме лучше раскрашивать код.
ext install jeff-hykin.better-shellscript-syntax
ℹ️ Расширение не будет работать с стандартной темой (не будет эффекта), но всё будет хорошо в таких темах как: Material Theme, Gruvbox, XD Theme и подобных.
При этом может не очень хорошо работать с вашей любимой, нестандартной темой оформления, перед использованием, проверьте всё ли вас устраивает.
Примеры
Вот мы и закончили с обзором утилит и расширений. Последние два (Txt Syntax и Better Shell Syntax) несут больше косметический характер, и их можно смело пропустить, чего не могу сказать про весь оставшийся список, рекомендую хотя бы попробовать их на практике.
Для удобства установки, все расширения собраны в один пакет Shell script IDE, правда бинарные зависимости (bashdb и hadolint) придется устанавливать самостоятельно.
ext install woozy-masta.shell-script-ide
Отладка
Хорошо когда настроенная IDE есть под рукой, но не всегда бывает так, к примеру мы работаем на удаленном сервере или в контейнере. По этому затронем тему настройки окружения для отладки и немного коснемся её самой.
Когда что-то идет не по плану, вам нужно определить, что именно вызывает сбой сценария. Bash предоставляет возможность для отладки, это запуск подоболочки с параметром -x
, который запускает весь сценарий в режиме отладки. Следы каждой команды плюс ее аргументы выводятся на стандартный вывод после того, как команды были развернуты, но до их выполнения.
Еще немного про ключи для отладки
Параметр отладки может быть установлен в произвольном месте в теле скрипта. Для отладки определенного блока кода, установим перед кодомset -x
, а для выхода из отладки при достижении конца отлаживаемого блока, обратим параметр вызвав set +x
.
Минус используется для активации опций оболочки, а плюс для деактивации. Пусть это вас не смущает.
Параметры которые вам скорее всего понадобятся для отладки:
|
Отключить получение имени файла с использованием метасимволов (подстановка). |
|
Печатает строки ввода оболочки по мере их чтения. Листинг скрипта будет предварительно выводиться на экран перед командами. |
|
Печатает трассировку команд перед выполнением команды. |
|
Не исполнять сценарий, а только проверить на наличие синтаксических ошибок. Проверка будет выполнена только для грубых ошибок, надежнее использовать shellchek. |
Также длинные параметры следующие за set -o
могут быть переданы через переменную SHELLOPTS
или используя родную для bash команду shopt
.
В shopt включение или отключение опций происходит при помощи флагов:
-
-s (set)
— установить опцию; -
-u (unset)
— отключить опцию.
Для того что бы отобразить текущие настройки параметров, выполните set -o
или shopt
Для экспериментов, давайте создадим простой скрипт.
Скрипт test.sh
#!/usr/bin/env bash
set -eu
function print-msg () {
printf '%b%-20s%b' "${colors[${1:-0}]}" "${@:2}" "${colors[0]}"
}
function random-color-echo() {
print-msg $((1 + RANDOM % $((4 - 1)))) "${*:-}"
}
function msg () {
random-color-echo "Hi ${*:-}!"
}
colors=(
"$(tput sgr0)" # reset
"$(tput setaf 1)" # red
"$(tput setaf 2)" # green
"$(tput setaf 3)" # yellow
"$(tput setaf 4)" # blue
)
for item in {"Bob","Alice"}; do
echo "$({ msg "$item"; ( date '+%s%N' ); } & wait)"
done
echo 'Done'
И выполним его при помощи bash -x ./test.sh
или добавив set -x
в начало скрипта:
Замечательно, теперь мы видим как работает наш скрипт. Стоит только пояснить, что означает +
, во первых, как вы догадались, за ним следует трассировка команды из скрипта, а вот количество знаков меняется и оно обозначает несколько уровней косвенного обращения.
Для небольших блоков логики этого зачастую достаточно, но, что если хочется большего? И первое, что мы можем сделать, это добавить необходимую информацию в параметр PS4
:
# Levels of indirection and time
PS4='+11[e[3;34m]t[e[0m]'
# User ID [Effective user ID]: Groups of user is a member
PS4+=' [e[0;35m]$UID[$EUID]:$GROUPS[e[0m] '
# Shell level and subshell
PS4+='11[e[1;31m]L$SHLVL:S$BASH_SUBSHELL[e[0m]'
# Source file
PS4+=' [e[1;33m]${BASH_SOURCE:-$0}[e[0m]'
# Line number
PS4+='[e[0;36m]#:${LINENO}[e[0m]'
# Function name
PS4+='11[e[1;32m]${FUNCNAME[0]:+${FUNCNAME[0]}(): }[e[0m]'
# Executed command
PS4+='n# '
export PS4
ℹ️ Объявить
PS4
вы можете в своем~/.bashrc
и он будет с вами постоянно, или определить свой формат отладки непосредственно в теле самого скрипта, или временно экспортировать изменения на время жизни оболочки bash.
О назначении параметров: PS0, PS1, PS2, PS3 и PS4
-
PS0
— Значение этого параметра раскрывается и отображается интерактивными оболочками после прочтения команды и до ее выполнения. Т.е. это будет напечатано перед исполнением каждой команды, по умолчанию не установлено. -
PS1
— Значение этого параметра раскрывается и используется в качестве основной строки приглашения. Это ваше стандартное приветствиеuser@host:~
-
PS2
— Значение этого параметра раскрывается, как и в случае сPS1
, и используется в качестве дополнительной строки приглашения. -
PS3
— Значение этого параметра используется в качестве подсказки для командыselect
. -
PS4
— Значение этого параметра расширяется, как в случае с PS1, и значение печатается перед отображением каждой команды bash во время трассировки выполнения. Первый символ расширенного значения PS4 при необходимости повторяется несколько раз, чтобы указать несколько уровней косвенного обращения. По умолчанию+
И снова запустив скрипт, мы увидим уже немного другой результат:
Давайте разберем этот пример, а в дальнейшем вы сами сможете реализовать удобный вывод отладочной информации под ваши нужды.
-
+
— Первый символ, отображает уровни косвенного обращения к командам, эта часть осталась как в оригинальномPS4
. -
t
— Текущее время, полезно для изучения тайминга команд, может быть заменено к примеру командойdate '+%x %X:%N %z'
для более подробного информирования, включая отображение наносекунд. -
$UID[$EUID]:$GROUPS
— Выведем ID и эффективный ID пользователя, перечисляем группы, членом которых является текущий пользователь. Это будет полезно для скриптов выполняющих действия от разных пользователей. -
L$SHLVL:S$BASH_SUBSHELL
— Отображения уровня оболочки, и уровня вложенной подоболочкой. Когда вы запускаете команду в оболочке, она запускается на уровне, называемом уровнем оболочки. Внутри оболочки вы можете открыть другую оболочку, которая делает её подоболочкой, или оболочку, которая её открыла.-
Уровень оболочки
SHLVL
поможет понять насколько глубоко вы находитесь в дочерних сессиях, ведь у каждой последующей оболочки могут быть добавлены или переопределены важные вам параметры. -
Уровень подоболочки
BASH_SUBSHELL
позволяет отслеживать все дочерние вызванные оболочки, к примеру, дочерняя оболочка не может вернуть переменную в родительскую оболочку.
-
-
${BASH_SOURCE:-$0}
— Имя исполняемого файла или функции. -
#:${LINENO}
— Номер трассируемой строки. -
${FUNCNAME[0]:+${FUNCNAME[0]}(): }
— Имя функции в рамках которой происходит исполнение.
ℹ️ Подробную информацию о параметрах вы всегда найдете в
man bash
разделах PROMPTING и PARAMETERS/Shell Variables
Если вы обрабатываете вывод скрипта на лету или объем отладочного лога очень велик, было бы удобно направить трассировку в отдельный файл. Для этих целей существует параметр BASH_XTRACEFD
, он позволяет указать номер файлового дескриптора для вывода сообщений трассировки.
Для этого мы создадим ссылку для файлового дескриптора с номером 3 на файл debug_$0.log
где $0
это имя bash сценария, а переменной BASH_XTRACEFD
передадим номер нашего нового дескриптора.
set -x
exec 3> "debug_$0.log"
BASH_XTRACEFD="3"
Также имеется возможность перенаправить вывод не в файл, а в утилиту, к примеру отправив сообщения утилите logger
мы сможем обратится к журналу при помощи командыjournalctl -t test.sh
.
set -x
exec 3> >(logger -t "$0")
BASH_XTRACEFD="3"
Теперь мы знаем как можно сделать отладку для всего сценария, или только для отдельной его части. Существует ли возможность принудительно исключить из отладки одну функцию? Да, и для этого достаточно в начало функции добавить такую конструкцию:
function some () {
{ local -; set +x; } 2>/dev/null
echo 'Do some stuff'
}
На тот случай если стандартной трассировки bash вам недостаточно, нужно получить больше информации о работе скрипта, выполнить более тонкое профилирование работы или разобраться с зависаниями, обратитесь к таким системным инструментам как strace
или в очень специфичной ситуации gdb
(надеюсь с вами этого не произойдет)
Пример запуска отладки скрипта при помощи strace
:
strace -C -f bash -x ./test.sh
Благодарю за ваше время и внимание, эффективного bash скриптинга вам!
Присоединяйтесь в телеграмм канал, где я периодически публикую заметки на тему DevOps, SRE и архитектурных решений.
With the arrival of Windows 10’s Bash shell, you can now create and run Bash shell scripts on Windows 10. You can also incorporate Bash commands into a Windows batch file or PowerShell script.
Even if you know what you’re doing, this isn’t necessarily as simple as it seems. Windows and UNIX use different end-of-line characters, and the Windows file system is accessible in a different location in the Bash environment.
How to Write a Bash Script on Windows 10
RELATED: How to Install and Use the Linux Bash Shell on Windows 10
When writing shell scripts on Windows, bear in mind that Windows and UNIX-like systems like Linux use different “end of line” characters in text files in shell scripts.
In other words, this means that you can’t simply write a shell script in Notepad. Save the file in Notepad and it won’t be interpreted properly by Bash. However, you can use more advanced text editors–for example, Notepad++ allows you to give a file UNIX end-of-line characters by clicking Edit > EOL Conversion > UNIX/OSX Format.
However, you’re better off just writing the shell script in the Bash environment itself. The Ubuntu-based Bash environment comes with both the vi and nano text editors. The vi editor is more powerful, but if you’ve never used it before, you may want to start with nano. It’s easier to use if you’re new.
For example, to create a bash script in nano, you’d run the following command in bash:
nano ~/myscript.sh
This would open the Nano text editor pointed at a file named “myscript.sh” in your user account’s home directory. (The “~” character represents your home directory, so the full path is /home/username/myscript.sh.)
Start your shell script with the line:
#!/bin/bash
RELATED: The Beginner’s Guide to Shell Scripting: The Basics
Enter the commands you want to run, each one on its own line. The script will run each command in turn. Add a “#” character before a line to treat it as a “comment”, something which helps you and other people understand the script but which isn’t run as a command. For more advanced tricks, consult a more detailed guide to Bash scripts on Linux. The same techniques will work in Bash on Ubuntu on Windows.
Note that there’s no way to run Windows programs from within the Bash environment. You’re restricted to Linux terminal commands and utilities, just as you would be on a typical Linux system.
We’ll use a basic “hello world” script as an example here:
#!/bin/bash # set the STRING variable STRING="Hello World!" # print the contents of the variable on screen echo $STRING
If you’re using the Nano text editor, you can save the file by pressing Ctrl+O and then Enter. Close the editor by pressing Ctrl+X.
Make the Script Executable and then Run It
You’ll probably want the make the script executable so you can run it more easily. On Linux, that means you need to give the script file the executable permission. To do so, run the following command in the terminal, pointing it at your script:
chmod +x ~/myscript.sh
To run the script, you can now just run it in the terminal by typing its path. Whenever you want to launch the script in the future, just open the Bash shell and type the path to the script.
~/myscript.sh
(If the script is in the current directory, you can run it with ./myscript.sh)
How to Work With Windows Files in a Bash Script
RELATED: How to Access Your Ubuntu Bash Files in Windows (and Your Windows System Drive in Bash)
To access Windows files in the script, you’ll need to specify their path under /mnt/c, not their Windows path. For example, if you wanted to specify the C:UsersBobDownloadstest.txt file, you’d need to specify the /mnt/c/Users/Bob/Downloads/test.txt path. Consult our guide to file locations in Windows 10’s Bash shell for more details.
How to Incorporate Bash Commands into a Batch or PowerShell Script
RELATED: How to Set Your Default Linux Distribution on Windows 10
Lastly, if you have an existing batch file or PowerShell script you want to incorporate commands into, you can run Bash commands directly using the bash -c
command.
For example, to run a Linux command in a Command Prompt or PowerShell window, you can run the following command:
bash -c "command"
This trick allows you to add Bash commands into batch files or PowerShell scripts. The Bash shell window will appear when a Bash command is running.
Update: If you have multiple Linux environments installed, you can use the wslconfig command to choose the default Linux environment used when you run the bash -c
command.
To create a shortcut to a Bash script from within Windows, just create a shortcut like normal. For the shortcut’s target, use the bash -c
command we outlined above and point it at the Bash script you created.
For example, you’d point a shortcut at ” bash -c "~/myscript.sh"
” to run the example script above. You can also just run this command from a Command Prompt or PowerShell window, too.
READ NEXT
- › How to Use Double Bracket Conditional Tests in Linux
- › Everything You Can Do With Windows 10’s New Bash Shell
- › How to Run Two or More Terminal Commands at Once in Linux
- › How to Run Windows Programs from Windows 10’s Bash Shell
- › Samsung’s New OLED Monitor Is Huge and Ultrawide
- › How to Disable Hibernation on Windows 10
- › Get the Latest Amazon Kindle for the Best Price Yet
- › Apple TV+, AirPlay, and Apple Music Are Coming to More TVs
Сегодня поговорим о bash-скриптах. Это — сценарии командной строки, написанные для оболочки bash. Существуют и другие оболочки, например — zsh, tcsh, ksh, но мы сосредоточимся на bash. Этот материал предназначен для всех желающих, единственное условие — умение работать в командной строке Linux.
Сценарии командной строки — это наборы тех же самых команд, которые можно вводить с клавиатуры, собранные в файлы и объединённые некоей общей целью. При этом результаты работы команд могут представлять либо самостоятельную ценность, либо служить входными данными для других команд. Сценарии — это мощный способ автоматизации часто выполняемых действий.
Итак, если говорить о командной строке, она позволяет выполнить несколько команд за один раз, введя их через точку с запятой:
pwd ; whoami
На самом деле, если вы опробовали это в своём терминале, ваш первый bash-скрипт, в котором задействованы две команды, уже написан. Работает он так. Сначала команда pwd
выводит на экран сведения о текущей рабочей директории, потом команда whoami
показывает данные о пользователе, под которым вы вошли в систему.
Используя подобный подход, вы можете совмещать сколько угодно команд в одной строке, ограничение — лишь в максимальном количестве аргументов, которое можно передать программе. Определить это ограничение можно с помощью такой команды:
getconf ARG_MAX
Командная строка — отличный инструмент, но команды в неё приходится вводить каждый раз, когда в них возникает необходимость. Что если записать набор команд в файл и просто вызывать этот файл для их выполнения? Собственно говоря, тот файл, о котором мы говорим, и называется сценарием командной строки.
Создайте пустой файл с использованием команды touch
. В его первой строке нужно указать, какую именно оболочку мы собираемся использовать. Нас интересует bash
, поэтому первая строка файла будет такой:
#!/bin/bash
В других строках этого файла символ решётки используется для обозначения комментариев, которые оболочка не обрабатывает. Однако, первая строка — это особый случай, здесь решётка, за которой следует восклицательный знак (эту последовательность называют шебанг) и путь к bash
, указывают системе на то, что сценарий создан именно для bash
.
Команды оболочки отделяются знаком перевода строки, комментарии выделяют знаком решётки. Вот как это выглядит:
#!/bin/bash
# This is a comment
pwd
whoami
Тут, так же, как и в командной строке, можно записывать команды в одной строке, разделяя точкой с запятой. Однако, если писать команды на разных строках, файл легче читать. В любом случае оболочка их обработает.
Установка разрешений для файла сценария
Сохраните файл, дав ему имя myscript
, и работа по созданию bash-скрипта почти закончена. Сейчас осталось лишь сделать этот файл исполняемым, иначе, попытавшись его запустить, вы столкнётесь с ошибкой Permission denied
.
Попытка запуска файла сценария с неправильно настроенными разрешениями
Сделаем файл исполняемым:
chmod +x ./myscript
Теперь попытаемся его выполнить:
./myscript
После настройки разрешений всё работает как надо.
Успешный запуск bash-скрипта
Вывод сообщений
Для вывода текста в консоль Linux применяется команда echo
. Воспользуемся знанием этого факта и отредактируем наш скрипт, добавив пояснения к данным, которые выводят уже имеющиеся в нём команды:
#!/bin/bash
# our comment is here
echo "The current directory is:"
pwd
echo "The user logged in is:"
whoami
Вот что получится после запуска обновлённого скрипта.
Вывод сообщений из скрипта
Теперь мы можем выводить поясняющие надписи, используя команду echo
. Если вы не знаете, как отредактировать файл, пользуясь средствами Linux, или раньше не встречались с командой echo
, взгляните на этот материал.
Использование переменных
Переменные позволяют хранить в файле сценария информацию, например — результаты работы команд для использования их другими командами.
Нет ничего плохого в исполнении отдельных команд без хранения результатов их работы, но возможности такого подхода весьма ограничены.
Существуют два типа переменных, которые можно использовать в bash-скриптах:
- Переменные среды
- Пользовательские переменные
Переменные среды
Иногда в командах оболочки нужно работать с некими системными данными. Вот, например, как вывести домашнюю директорию текущего пользователя:
#!/bin/bash
# display user home
echo "Home for the current user is: $HOME"
Обратите внимание на то, что мы можем использовать системную переменную $HOME
в двойных кавычках, это не помешает системе её распознать. Вот что получится, если выполнить вышеприведённый сценарий.
Использование переменной среды в сценарии
А что если надо вывести на экран значок доллара? Попробуем так:
echo "I have $1 in my pocket"
Система обнаружит знак доллара в строке, ограниченной кавычками, и решит, что мы сослались на переменную. Скрипт попытается вывести на экран значение неопределённой переменной $1
. Это не то, что нам нужно. Что делать?
В подобной ситуации поможет использование управляющего символа, обратной косой черты, перед знаком доллара:
echo "I have $1 in my pocket"
Теперь сценарий выведет именно то, что ожидается.
Использование управляющей последовательности для вывода знака доллара
Пользовательские переменные
В дополнение к переменным среды, bash-скрипты позволяют задавать и использовать в сценарии собственные переменные. Подобные переменные хранят значение до тех пор, пока не завершится выполнение сценария.
Как и в случае с системными переменными, к пользовательским переменным можно обращаться, используя знак доллара:
#!/bin/bash
# testing variables
grade=5
person="Adam"
echo "$person is a good boy, he is in grade $grade"
Вот что получится после запуска такого сценария.
Пользовательские переменные в сценарии
Подстановка команд
Одна из самых полезных возможностей bash-скриптов — это возможность извлекать информацию из вывода команд и назначать её переменным, что позволяет использовать эту информацию где угодно в файле сценария.
Сделать это можно двумя способами.
- С помощью значка обратного апострофа «`»
- С помощью конструкции
$()
Используя первый подход, проследите за тем, чтобы вместо обратного апострофа не ввести одиночную кавычку. Команду нужно заключить в два таких значка:
mydir=`pwd`
При втором подходе то же самое записывают так:
mydir=$(pwd)
А скрипт, в итоге, может выглядеть так:
#!/bin/bash
mydir=$(pwd)
echo $mydir
В ходе его работы вывод команды pwd
будет сохранён в переменной mydir
, содержимое которой, с помощью команды echo
, попадёт в консоль.
Скрипт, сохраняющий результаты работы команды в переменной
Математические операции
Для выполнения математических операций в файле скрипта можно использовать конструкцию вида $((a+b))
:
#!/bin/bash
var1=$(( 5 + 5 ))
echo $var1
var2=$(( $var1 * 2 ))
echo $var2
Математические операции в сценарии
Управляющая конструкция if-then
В некоторых сценариях требуется управлять потоком исполнения команд. Например, если некое значение больше пяти, нужно выполнить одно действие, в противном случае — другое. Подобное применимо в очень многих ситуациях, и здесь нам поможет управляющая конструкция if-then
. В наиболее простом виде она выглядит так:
if команда
then
команды
fi
А вот рабочий пример:
#!/bin/bash
if pwd
then
echo "It works"
fi
В данном случае, если выполнение команды pwd
завершится успешно, в консоль будет выведен текст «it works».
Воспользуемся имеющимися у нас знаниями и напишем более сложный сценарий. Скажем, надо найти некоего пользователя в /etc/passwd
, и если найти его удалось, сообщить о том, что он существует.
#!/bin/bash
user=likegeeks
if grep $user /etc/passwd
then
echo "The user $user Exists"
fi
Вот что получается после запуска этого скрипта.
Поиск пользователя
Здесь мы воспользовались командой grep
для поиска пользователя в файле /etc/passwd
. Если команда grep
вам незнакома, её описание можно найти здесь.
В этом примере, если пользователь найден, скрипт выведет соответствующее сообщение. А если найти пользователя не удалось? В данном случае скрипт просто завершит выполнение, ничего нам не сообщив. Хотелось бы, чтобы он сказал нам и об этом, поэтому усовершенствуем код.
Управляющая конструкция if-then-else
Для того, чтобы программа смогла сообщить и о результатах успешного поиска, и о неудаче, воспользуемся конструкцией if-then-else
. Вот как она устроена:
if команда
then
команды
else
команды
fi
Если первая команда возвратит ноль, что означает её успешное выполнение, условие окажется истинным и выполнение не пойдёт по ветке else
. В противном случае, если будет возвращено что-то, отличающееся от нуля, что будет означать неудачу, или ложный результат, будут выполнены команды, расположенные после else
.
Напишем такой скрипт:
#!/bin/bash
user=anotherUser
if grep $user /etc/passwd
then
echo "The user $user Exists"
else
echo "The user $user doesn’t exist"
fi
Его исполнение пошло по ветке else
.
Запуск скрипта с конструкцией if-then-else
Ну что же, продолжаем двигаться дальше и зададимся вопросом о более сложных условиях. Что если надо проверить не одно условие, а несколько? Например, если нужный пользователь найден, надо вывести одно сообщение, если выполняется ещё какое-то условие — ещё одно сообщение, и так далее. В подобной ситуации нам помогут вложенные условия. Выглядит это так:
if команда1
then
команды
elif команда2
then
команды
fi
Если первая команда вернёт ноль, что говорит о её успешном выполнении, выполнятся команды в первом блоке then
, иначе, если первое условие окажется ложным, и если вторая команда вернёт ноль, выполнится второй блок кода.
#!/bin/bash
user=anotherUser
if grep $user /etc/passwd
then
echo "The user $user Exists"
elif ls /home
then
echo "The user doesn’t exist but anyway there is a directory under /home"
fi
В подобном скрипте можно, например, создавать нового пользователя с помощью команды useradd
, если поиск не дал результатов, или делать ещё что-нибудь полезное.
Сравнение чисел
В скриптах можно сравнивать числовые значения. Ниже приведён список соответствующих команд.
n1 -eq n2
Возвращает истинное значение, если n1
равно n2
.
n1 -ge n2
Возвращает истинное значение, если n1
больше или равно n2
.
n1 -gt n2
Возвращает истинное значение, если n1
больше n2
.
n1 -le n2
Возвращает истинное значение, если n1
меньше или равно n2
.
n1 -lt n2
Возвращает истинное значение, если n1 меньше n2
.
n1 -ne n2
Возвращает истинное значение, если n1
не равно n2
.
В качестве примера опробуем один из операторов сравнения. Обратите внимание на то, что выражение заключено в квадратные скобки.
#!/bin/bash
val1=6
if [ $val1 -gt 5 ]
then
echo "The test value $val1 is greater than 5"
else
echo "The test value $val1 is not greater than 5"
fi
Вот что выведет эта команда.
Сравнение чисел в скриптах
Значение переменной val1
больше чем 5, в итоге выполняется ветвь then
оператора сравнения и в консоль выводится соответствующее сообщение.
Сравнение строк
В сценариях можно сравнивать и строковые значения. Операторы сравнения выглядят довольно просто, однако у операций сравнения строк есть определённые особенности, которых мы коснёмся ниже. Вот список операторов.
str1 = str2
Проверяет строки на равенство, возвращает истину, если строки идентичны.
str1 != str2
Возвращает истину, если строки не идентичны.
str1 < str2
Возвращает истину, еслиstr1
меньше, чемstr2
.
str1 > str2
Возвращает истину, еслиstr1
больше, чемstr2
.
-n str1
Возвращает истину, если длинаstr1
больше нуля.
-z str1
Возвращает истину, если длинаstr1
равна нулю.
Вот пример сравнения строк в сценарии:
#!/bin/bash
user ="likegeeks"
if [$user = $USER]
then
echo "The user $user is the current logged in user"
fi
В результате выполнения скрипта получим следующее.
Сравнение строк в скриптах
Вот одна особенность сравнения строк, о которой стоит упомянуть. А именно, операторы «>» и «<» необходимо экранировать с помощью обратной косой черты, иначе скрипт будет работать неправильно, хотя сообщений об ошибках и не появится. Скрипт интерпретирует знак «>» как команду перенаправления вывода.
Вот как работа с этими операторами выглядит в коде:
#!/bin/bash
val1=text
val2="another text"
if [ $val1 > $val2 ]
then
echo "$val1 is greater than $val2"
else
echo "$val1 is less than $val2"
fi
Вот результаты работы скрипта.
Сравнение строк, выведенное предупреждение
Обратите внимание на то, что скрипт, хотя и выполняется, выдаёт предупреждение:
./myscript: line 5: [: too many arguments
Для того, чтобы избавиться от этого предупреждения, заключим $val2
в двойные кавычки:
#!/bin/bash
val1=text
val2="another text"
if [ $val1 > "$val2" ]
then
echo "$val1 is greater than $val2"
else
echo "$val1 is less than $val2"
fi
Теперь всё работает как надо.
Сравнение строк
Ещё одна особенность операторов «>» и «<» заключается в том, как они работают с символами в верхнем и нижнем регистрах. Для того, чтобы понять эту особенность, подготовим текстовый файл с таким содержимым:
Likegeeks
likegeeks
Сохраним его, дав имя myfile
, после чего выполним в терминале такую команду:
sort myfile
Она отсортирует строки из файла так:
likegeeks
Likegeeks
Команда sort
, по умолчанию, сортирует строки по возрастанию, то есть строчная буква в нашем примере меньше прописной. Теперь подготовим скрипт, который будет сравнивать те же строки:
#!/bin/bash
val1=Likegeeks
val2=likegeeks
if [ $val1 > $val2 ]
then
echo "$val1 is greater than $val2"
else
echo "$val1 is less than $val2"
fi
Если его запустить, окажется, что всё наоборот — строчная буква теперь больше прописной.
Команда sort и сравнение строк в файле сценария
В командах сравнения прописные буквы меньше строчных. Сравнение строк здесь выполняется путём сравнения ASCII-кодов символов, порядок сортировки, таким образом, зависит от кодов символов.
Команда sort
, в свою очередь, использует порядок сортировки, заданный в настройках системного языка.
Проверки файлов
Пожалуй, нижеприведённые команды используются в bash-скриптах чаще всего. Они позволяют проверять различные условия, касающиеся файлов. Вот список этих команд.
-d file
Проверяет, существует ли файл, и является ли он директорией.
-e file
Проверяет, существует ли файл.
-f file
Проверяет, существует ли файл, и является ли он файлом.
-r file
Проверяет, существует ли файл, и доступен ли он для чтения.
-s file П
роверяет, существует ли файл, и не является ли он пустым.
-w file
Проверяет, существует ли файл, и доступен ли он для записи.
-x file
Проверяет, существует ли файл, и является ли он исполняемым.
file1 -nt file2
Проверяет, новее ли file1
, чем file2
.
file1 -ot file2
Проверяет, старше ли file1
, чем file2
.
-O file
Проверяет, существует ли файл, и является ли его владельцем текущий пользователь.
-G file
Проверяет, существует ли файл, и соответствует ли его идентификатор группы идентификатору группы текущего пользователя.
Эти команды, как впрочем, и многие другие рассмотренные сегодня, несложно запомнить. Их имена, являясь сокращениями от различных слов, прямо указывают на выполняемые ими проверки.
Опробуем одну из команд на практике:
#!/bin/bash
mydir=/home/likegeeks
if [ -d $mydir ]
then
echo "The $mydir directory exists"
cd $ mydir
ls
else
echo "The $mydir directory does not exist"
fi
Этот скрипт, для существующей директории, выведет её содержимое.
Вывод содержимого директории
Полагаем, с остальными командами вы сможете поэкспериментировать самостоятельно, все они применяются по тому же принципу.
Итоги
Сегодня мы рассказали о том, как приступить к написанию bash-скриптов и рассмотрели некоторые базовые вещи. На самом деле, тема bash-программирования огромна. Эта статья является переводом первой части большой серии из 11 материалов. Если вы хотите продолжения прямо сейчас — вот список оригиналов этих материалов. Для удобства сюда включён и тот, перевод которого вы только что прочли.
Bash-скрипты, часть 2: циклы
Циклы for
Оболочка bash поддерживает циклы for
, которые позволяют организовывать перебор последовательностей значений. Вот какова базовая структура таких циклов:
for var in list
do
команды
done
В каждой итерации цикла в переменную var
будет записываться следующее значение из списка list
. В первом проходе цикла, таким образом, будет задействовано первое значение из списка. Во втором — второе, и так далее — до тех пор, пока цикл не дойдёт до последнего элемента.
Перебор простых значений
Пожалуй, самый простой пример цикла for
в bash-скриптах — это перебор списка простых значений:
#!/bin/bash
for var in first second third fourth fifth
do
echo The $var item
done
Ниже показаны результаты работы этого скрипта. Хорошо видно, что в переменную $var
последовательно попадают элементы из списка. Происходит так до тех пор, пока цикл не дойдёт до последнего из них.
Обратите внимание на то, что переменная $var
сохраняет значение при выходе из цикла, её содержимое можно менять, в целом, работать с ней можно как с любой другой переменной.
Перебор сложных значений
В списке, использованном при инициализации цикла for
, могут содержаться не только простые строки, состоящие из одного слова, но и целые фразы, в которые входят несколько слов и знаков препинания. Например, всё это может выглядеть так:
#!/bin/bash
for var in first "the second" "the third" "I’ll do it"
do
echo "This is: $var"
done
Вот что получится после того, как этот цикл пройдётся по списку. Как видите, результат вполне ожидаем.
Инициализация цикла списком, полученным из результатов работы команды
Ещё один способ инициализации цикла for
заключается в передаче ему списка, который является результатом работы некоей команды. Тут используется подстановка команд для их исполнения и получения результатов их работы.
#!/bin/bash
file="myfile"
for var in $(cat $file)
do
echo " $var"
done
В этом примере задействована команда cat
, которая читает содержимое файла. Полученный список значений передаётся в цикл и выводится на экран. Обратите внимание на то, что в файле, к которому мы обращаемся, содержится список слов, разделённых знаками перевода строки, пробелы при этом не используются.
Тут надо учесть, что подобный подход, если ожидается построчная обработка данных, не сработает для файла более сложной структуры, в строках которого может содержаться по несколько слов, разделённых пробелами. Цикл будет обрабатывать отдельные слова, а не строки.
Что, если это совсем не то, что нужно?
Разделители полей
Причина вышеописанной особенности заключается в специальной переменной окружения, которая называется IFS
(Internal Field Separator) и позволяет указывать разделители полей. По умолчанию оболочка bash считает разделителями полей следующие символы:
- Пробел
- Знак табуляции
- Знак перевода строки
Если bash встречает в данных любой из этих символов, он считает, что перед ним — следующее самостоятельное значение списка.
Для того, чтобы решить проблему, можно временно изменить переменную среды IFS
. Вот как это сделать в bash-скрипте, если исходить из предположения, что в качестве разделителя полей нужен только перевод строки:
IFS=$'n'
После добавления этой команды в bash-скрипт, он будет работать как надо, игнорируя пробелы и знаки табуляции, считая разделителями полей лишь символы перевода строки.
#!/bin/bash
file="/etc/passwd"
IFS=$'n'
for var in $(cat $file)
do
echo " $var"
done
Если этот скрипт запустить, он выведет именно то, что от него требуется, давая, в каждой итерации цикла, доступ к очередной строке, записанной в файл.
Разделителями могут быть и другие символы. Например, выше мы выводили на экран содержимое файла /etc/passwd
. Данные о пользователях в строках разделены с помощью двоеточий. Если в цикле нужно обрабатывать подобные строки, IFS
можно настроить так:
IFS=:
Обход файлов, содержащихся в директории
Один из самых распространённых вариантов использования циклов for
в bash-скриптах заключается в обходе файлов, находящихся в некоей директории, и в обработке этих файлов.
Например, вот как можно вывести список файлов и папок:
#!/bin/bash
for file in /home/likegeeks/*
do
if [ -d "$file" ]
then
echo "$file is a directory"
elif [ -f "$file" ]
then
echo "$file is a file"
fi
done
Если вы разобрались с материалом выше, вам должно быть понятно устройство конструкции if-then
, а так же то, как отличить файл от папки. Если вам сложно понять вышеприведённый код, перечитайте этот материал.
Вот что выведет скрипт.
Обратите внимание на то, как мы инициализируем цикл, а именно — на подстановочный знак «*» в конце адреса папки. Этот символ можно воспринимать как шаблон, означающий: «все файлы с любыми именами». он позволяет организовать автоматическую подстановку имён файлов, которые соответствуют шаблону.
При проверке условия в операторе if
, мы заключаем имя переменной в кавычки. Сделано это потому что имя файла или папки может содержать пробелы.
Циклы for в стиле C
Если вы знакомы с языком программирования C, синтаксис описания bash-циклов for
может показаться вам странным, так как привыкли вы, очевидно, к такому описанию циклов:
for (i = 0; i < 10; i++)
{
printf("number is %dn", i);
}
В bash-скриптах можно использовать циклы for
, описание которых выглядит очень похожим на циклы в стиле C, правда, без некоторых отличий тут не обошлось. Схема цикла при подобном подходе выглядит так:
for (( начальное значение переменной ; условие окончания цикла; изменение переменной ))
На bash это можно написать так:
for (( a = 1; a < 10; a++ ))
А вот рабочий пример:
#!/bin/bash
for (( i=1; i <= 10; i++ ))
do
echo "number is $i"
done
Этот код выведет список чисел от 1 до 10.
Цикл while
Конструкция for —
не единственный способ организации циклов в bash-скриптах. Здесь можно пользоваться и циклами while
. В таком цикле можно задать команду проверки некоего условия и выполнять тело цикла до тех пор, пока проверяемое условие возвращает ноль, или сигнал успешного завершения некоей операции. Когда условие цикла вернёт ненулевое значение, что означает ошибку, цикл остановится.
Вот схема организации циклов while
while команда проверки условия
do
другие команды
done
Взглянем на пример скрипта с таким циклом:
#!/bin/bash
var1=5
while [ $var1 -gt 0 ]
do
echo $var1
var1=$[ $var1 - 1 ]
done
На входе в цикл проверяется, больше ли нуля переменная $var1
. Если это так, выполняется тело цикла, в котором из значения переменной вычитается единица. Так происходит в каждой итерации, при этом мы выводим в консоль значение переменной до его модификации. Как только $var1
примет значение 0, цикл прекращается.
Если не модифицировать переменную $var1
, это приведёт к попаданию скрипта в бесконечный цикл.
Вложенные циклы
В теле цикла можно использовать любые команды, в том числе — запускать другие циклы. Такие конструкции называют вложенными циклами:
#!/bin/bash
for (( a = 1; a <= 3; a++ ))
do
echo "Start $a:"
for (( b = 1; b <= 3; b++ ))
do
echo " Inner loop: $b"
done
done
Ниже показано то, что выведет этот скрипт. Как видно, сначала выполняется первая итерация внешнего цикла, потом — три итерации внутреннего, после его завершения снова в дело вступает внешний цикл, потом опять — внутренний.
Обработка содержимого файла
Чаще всего вложенные циклы используют для обработки файлов. Так, внешний цикл занимается перебором строк файла, а внутренний уже работает с каждой строкой. Вот, например, как выглядит обработка файла /etc/passwd
:
#!/bin/bash
IFS=$'n'
for entry in $(cat /etc/passwd)
do
echo "Values in $entry –"
IFS=:
for value in $entry
do
echo " $value"
done
done
В этом скрипте два цикла. Первый проходится по строкам, используя в качестве разделителя знак перевода строки. Внутренний занят разбором строк, поля которых разделены двоеточиями.
Такой подход можно использовать при обработке файлов формата CSV, или любых подобных файлов, записывая, по мере надобности, в переменную окружения IFS
символ-разделитель.
Управление циклами
Возможно, после входа в цикл, нужно будет остановить его при достижении переменной цикла определённого значения, которое не соответствует изначально заданному условию окончания цикла. Надо ли будет в такой ситуации дожидаться нормального завершения цикла? Нет конечно, и в подобных случаях пригодятся следующие две команды:
break
continue
Команда break
Эта команда позволяет прервать выполнение цикла. Её можно использовать и для циклов for
, и для циклов while
:
#!/bin/bash
for var1 in 1 2 3 4 5 6 7 8 9 10
do
if [ $var1 -eq 5 ]
then
break
fi
echo "Number: $var1"
done
Такой цикл, в обычных условиях, пройдётся по всему списку значений из списка. Однако, в нашем случае, его выполнение будет прервано, когда переменная $var1
будет равна 5.
Вот — то же самое, но уже для цикла while
:
#!/bin/bash
var1=1
while [ $var1 -lt 10 ]
do
if [ $var1 -eq 5 ]
then
break
fi
echo "Iteration: $var1"
var1=$(( $var1 + 1 ))
done
Команда break
, исполненная, когда значение $var1
станет равно 5, прерывает цикл. В консоль выведется то же самое, что и в предыдущем примере.
Команда continue
Когда в теле цикла встречается эта команда, текущая итерация завершается досрочно и начинается следующая, при этом выхода из цикла не происходит. Посмотрим на команду continue
в цикле for
:
#!/bin/bash
for (( var1 = 1; var1 < 15; var1++ ))
do
if [ $var1 -gt 5 ] && [ $var1 -lt 10 ]
then
continue
fi
echo "Iteration number: $var1"
done
Когда условие внутри цикла выполняется, то есть, когда $var1
больше 5 и меньше 10, оболочка исполняет команду continue
. Это приводит к пропуску оставшихся в теле цикла команд и переходу к следующей итерации.
Обработка вывода, выполняемого в цикле
Данные, выводимые в цикле, можно обработать, либо перенаправив вывод, либо передав их в конвейер. Делается это с помощью добавления команд обработки вывода после инструкции done
.
Например, вместо того, чтобы показывать на экране то, что выводится в цикле, можно записать всё это в файл или передать ещё куда-нибудь:
#!/bin/bash
for (( a = 1; a < 10; a++ ))
do
echo "Number is $a"
done > myfile.txt
echo "finished."
Оболочка создаст файл myfile.txt
и перенаправит в этот файл вывод конструкции for
. Откроем файл и удостоверимся в том, что он содержит именно то, что ожидается.
Пример: поиск исполняемых файлов
Давайте воспользуемся тем, что мы уже разобрали, и напишем что-нибудь полезное. Например, если надо выяснить, какие именно исполняемые файлы доступны в системе, можно просканировать все папки, записанные в переменную окружения PATH
. Весь арсенал средств, который для этого нужен, у нас уже есть, надо лишь собрать всё это воедино:
#!/bin/bash
IFS=:
for folder in $PATH
do
echo "$folder:"
for file in $folder/*
do
if [ -x $file ]
then
echo " $file"
fi
done
done
Такой вот скрипт, небольшой и несложный, позволил получить список исполняемых файлов, хранящихся в папках из PATH
.
Итоги
Сегодня мы поговорили о циклах for
и while
в bash-скриптах, о том, как их запускать, как ими управлять. Теперь вы умеете обрабатывать в циклах строки с разными разделителями, знаете, как перенаправлять данные, выведенные в циклах, в файлы, как просматривать и анализировать содержимое директорий.
Если предположить, что вы — разработчик bash-скриптов, который знает о них только то, что изложено в в самом начале этой статьи, то вы уже вполне можете написать кое-что полезное. Далее вы узнаете, как передавать bash-скриптам параметры и ключи командной строки, и что с этим всем делать.
Возможно вам понравится:
Руководство по установке сервера 1С на Linux
Справочник по командной строке iptables (шпаргалка)
источник
original
How can I run .sh on Windows 7 Command Prompt? I always get this error when I try to run this line in it,
app/build/build.sh
error,
'app' is not recognized...
or,
bash app/build/build.sh
error,
'bash' is not recognized...
Any ideas what have I missed?
Here the screen grab,
asked Oct 23, 2014 at 6:59
1
Install GIT. During installation of GIT, add GIT Bash to windows context menu by selecting its option. After installation right click in your folder select GIT Bash Here
(see attached pic) and use your sh command like for example:
sh test.sh
answered May 27, 2016 at 8:15
Faisal MqFaisal Mq
4,7764 gold badges34 silver badges39 bronze badges
4
The error message indicates that you have not installed bash
, or it is not in your PATH
.
The top Google hit is http://win-bash.sourceforge.net/ but you also need to understand that most Bash scripts expect a Unix-like environment; so just installing Bash is probably unlikely to allow you to run a script you found on the net, unless it was specifically designed for this particular usage scenario. The usual solution to that is https://www.cygwin.com/ but there are many possible alternatives, depending on what exactly it is that you want to accomplish.
If Windows is not central to your usage scenario, installing a free OS (perhaps virtualized) might be the simplest way forward.
The second error message is due to the fact that Windows nominally accepts forward slash as a directory separator, but in this context, it is being interpreted as a switch separator. In other words, Windows parses your command line as app /build /build.sh
(or, to paraphrase with Unix option conventions, app --build --build.sh
). You could try appbuildbuild.sh
but it is unlikely to work, because of the circumstances outlined above.
answered Oct 23, 2014 at 7:33
tripleeetripleee
170k31 gold badges261 silver badges305 bronze badges
1
The most common way to run a .sh file is using the sh command:
C:>sh my-script-test.sh
other good option is installing CygWin
in Windows
the home
is located in:
C:cygwin64home[user]
for example i execute my my-script-test.sh
file using the bash command as:
jorgesys@INT024P ~$ bash /home/[user]/my-script-test.sh
answered Jun 9, 2015 at 23:09
JorgesysJorgesys
123k23 gold badges328 silver badges264 bronze badges
6
you can use also cmder
Cmder is a software package created out of pure frustration over the absence of nice console emulators on Windows. It is based on amazing software, and spiced up with the Monokai color scheme and a custom prompt layout, looking sexy from the start
cmder.net
answered Oct 18, 2016 at 17:05
vipmaavipmaa
1,00216 silver badges24 bronze badges
1
On Windows 10 Anniversary Update, it’s even easier to run shell commands in/with bash on ubuntu on windows
I was trying to set my region for my x-wrt r7000 netgear router, I found the following worked for me, using bash on ubuntu on windows, you do have to enable subsystem found in windows features, and dev mode on
ssh admin@192.168.1.1 < /mnt/c/ccode-eu.sh
answered Aug 25, 2016 at 21:10
SignedAdamSignedAdam
711 silver badge1 bronze badge
1
Install the GitBash
tool in the Windows OS. Set the below Path
in the environment variables of System for the Git installation.
<Program Files in C:>Gitbin
<Program Files in C:>Gitusrbin
Type ‘sh‘ in cmd
window to redirect into Bourne shell and run your commands in terminal.
answered Aug 21, 2020 at 19:36
TechRookieTechRookie
1411 silver badge4 bronze badges
1
Personally I used this batch file, but it does require CygWin installed (64-bit as shown).
Just associate the file type .SH with this batchfile (ExecSH.BAT in my case) and you can double-click on the .SH and it runs.
@echo off
setlocal
if not exist "%~dpn1.sh" echo Script "%~dpn1.sh" not found & goto :eof
set _CYGBIN=C:cygwin64bin
if not exist "%_CYGBIN%" echo Couldn't find Cygwin at "%_CYGBIN%" & goto :eof
:: Resolve ___.sh to /cygdrive based *nix path and store in %_CYGSCRIPT%
for /f "delims=" %%A in ('%_CYGBIN%cygpath.exe "%~dpn1.sh"') do set _CYGSCRIPT=%%A
for /f "delims=" %%A in ('%_CYGBIN%cygpath.exe "%CD%"') do set _CYGPATH=%%A
:: Throw away temporary env vars and invoke script, passing any args that were passed to us
endlocal & %_CYGBIN%mintty.exe -e /bin/bash -l -c 'cd %_CYGPATH%; %_CYGSCRIPT% %*'
Based on this original work.
answered Feb 21, 2017 at 12:23
AnonymouseAnonymouse
9379 silver badges20 bronze badges
I use Windows 10 Bash shell aka Linux Subsystem aka Ubuntu in Windows 10 as guided here
answered Apr 9, 2020 at 6:20
Nam G VUNam G VU
32.2k68 gold badges226 silver badges366 bronze badges
just install git and by «bash <name.sh>» run your .sh file.
answered Apr 3, 2021 at 8:51
Have you tried cd
ing to the root directory where your .sh
is located in order to execute it from there, instead of writing down a path to the file as you showed in your question?
Like so:
$ cd app/build
$ build.sh
answered May 28, 2021 at 6:59
D4V1DD4V1D
5,7353 gold badges29 silver badges64 bronze badges