bash.md

Личный сайт Go-разработчика из Казани

Bash — это командная оболочка unix, которая распространялась как оболочка для операционной системы GNU и используется в качестве оболочки по умолчанию для Linux и macOS. Почти все нижеприведённые примеры могут быть частью shell-скриптов или исполнены напрямую в shell.

Подробнее.

1#!/bin/bash 2# Первая строка скрипта — это шебанг, который сообщает системе, как исполнять 3# этот скрипт: https://ru.wikipedia.org/wiki/Шебанг_(Unix) 4# Как вы уже поняли, комментарии начинаются с «#». Шебанг — тоже комментарий. 5 6# Простой пример hello world: 7echo Hello world! 8 9# Отдельные команды начинаются с новой строки или разделяются точкой с запятой: 10echo 'Это первая строка'; echo 'Это вторая строка' 11# => Это первая строка 12# => Это вторая строка 13 14# Вот так объявляется переменная: 15VARIABLE="Просто строка" 16 17# но не так: 18VARIABLE = "Просто строка" 19# Bash решит, что VARIABLE - это команда, которую он должен исполнить, 20# и выдаст ошибку, потому что не сможет найти её. 21 22# и не так: 23VARIABLE= 'Просто строка' 24# Тут Bash решит, что 'Просто строка' — это команда, которую он должен 25# исполнить, и выдаст ошибку, потому что не сможет найти такой команды 26# (здесь 'VARIABLE=' выглядит как присвоение значения переменной, 27# но только в контексте исполнения команды 'Просто строка'). 28 29# Использование переменой: 30echo $VARIABLE # => Просто строка 31echo "$VARIABLE" # => Просто строка 32echo '$VARIABLE' # => $Variable 33# Когда вы используете переменную — присваиваете, экспортируете и т.д. — 34# пишите её имя без $. А для получения значения переменной используйте $. 35# Заметьте, что ' (одинарные кавычки) не раскрывают переменные в них. 36 37# Раскрытие параметров ${ }: 38echo ${Variable} # => Просто строка 39# Это простое использование раскрытия параметров 40# Раскрытие параметров получает значение переменной. 41# Оно «раскрывает», или печатает это значение. 42# ̶Значение можно изменить во время раскрытия. 43# Ниже приведены другие модификации при раскрытии параметров. 44 45# Замена подстрок в переменных 46echo ${Variable/Просто/Это} # => Это строка 47# Заменит первое вхождение «Просто» на «Это» 48 49# Взять подстроку из переменной 50LENGTH=7 51echo ${VARIABLE:0:LENGTH} # => Просто 52# Это выражение вернёт только первые 7 символов переменной VARIABLE 53echo ${Variable: -5} # => трока 54# Вернёт последние 5 символов (обратите внимание на пробел перед «-5») 55 56# Длина строки 57echo ${#Variable} # => 13 58 59# Значение переменной по умолчанию 60echo ${FOO:-"ЗначениеПоУмолчаниюЕслиFooПустаИлиНеНайдена"} 61# => ЗначениеПоУмолчаниюЕслиFooПустаИлиНеНайдена 62# Это сработает при отсутствующем значении (FOO=) и пустой строке (FOO=""); 63# ноль (FOO=0) вернёт 0. 64# Заметьте, что в любом случае это лишь вернёт значение по умолчанию, 65# а значение самой переменной FOO не изменится. 66 67# Объявить массив из 6 элементов 68array0=(один два три четыре пять шесть) 69# Вывести первый элемент 70echo $array0 # => "один" 71# Вывести первый элемент 72echo ${array0[0]} # => "один" 73# Вывести все элементы 74echo ${array0[@]} # => "один два три четыре пять шесть" 75# Вывести число элементов 76echo ${#array0[@]} # => "6" 77# Вывести число символов в третьем элементе 78echo ${#array0[2]} # => "3" 79# Вывести 2 элемента, начиная с четвёртого 80echo ${array0[@]:3:2} # => "четыре пять" 81# Вывести все элементы, каждый на своей строке 82for i in "${array0[@]}"; do 83 echo "$i" 84done 85 86# Раскрытие скобок { } 87# Используется для создания произвольных строк 88echo {1..10} # => 1 2 3 4 5 6 7 8 9 10 89echo {a..z} # => a b c d e f g h i j k l m n o p q r s t u v w x y z 90# Выведет диапазон от начального до конечного значения 91 92# Встроенные переменные: 93# В bash есть полезные встроенные переменные, например 94echo "Значение, возвращённое последней программой: $?" 95echo "Идентификатор процесса скрипта: $$" 96echo "Число аргументов, переданных скрипту: $#" 97echo "Все аргументы, переданные скрипту: $@" 98echo "Аргументы скрипта, распределённые по отдельным переменным: $1 $2..." 99 100# Теперь, когда мы знаем, как выводить и использовать переменные, 101# давайте изучим некоторые другие основы Bash! 102 103# Текущая директория доступна по команде `pwd`. 104# `pwd` расшифровывается как «print working directory», т.е. 105# «напечатать рабочую директорию». 106# Мы также можем использовать встроенную переменную `$PWD`. 107# Заметьте, следующие выражения эквивалентны: 108echo "Я в $(pwd)" # выполняет `pwd` и раскрывает вывод 109echo "Я в $PWD" # раскрывает переменную 110 111# Если вы получаете слишком много информации в терминале или из скрипта, 112# команда `clear` очистит экран 113clear 114# Очистить экран можно также с помощью Ctrl+L 115 116# Чтение аргументов с устройства ввода: 117echo "Как Вас зовут?" 118read NAME # Обратите внимание, что нам не нужно определять новую переменную 119echo Привет, $NAME! 120 121# У нас есть обычная структура if: 122# наберите 'man test' для получения подробной информации о форматах условия 123if [ $NAME != $USER ] 124then 125 echo "Имя не совпадает с именем пользователя" 126else 127 echo "Имя совпадает с именем пользователя" 128fi 129# Истинно, если значение $Name не совпадает с текущим именем пользователя 130 131# Примечание: если $Name пуста, bash интерпретирует код так: 132if [ != $USER ] 133# а это ошибочная команда 134# поэтому «безопасный» способ использовать пустые переменные в Bash таков: 135if [ "$Name" != $USER ] ... 136# при этом, когда $Name пуста, bash видит код так: 137if [ "" != $USER ] ... 138# что работает правильно 139 140# Также есть условное исполнение 141echo "Исполнится всегда" || echo "Исполнится, если первая команда завершится ошибкой" 142# => Исполнится всегда 143echo "Исполнится всегда" && echo "Исполнится, если первая команда выполнится удачно" 144# => Исполнится всегда 145# => Исполнится, если первая команда выполнится удачно 146 147 148# Чтобы использовать && и || в выражениях if, нужно несколько пар скобок: 149if [ $NAME == "Стив" ] && [ $AGE -eq 15 ] 150then 151 echo "Исполнится, если $NAME равно Стив И $AGE равно 15." 152fi 153 154if [ $NAME == "Дания" ] || [ $NAME == "Зак" ] 155then 156 echo "Исполнится, если $NAME равно Дания ИЛИ Зак." 157fi 158 159# Есть ещё оператор «=~», который проверяет строку 160# на соответствие регулярному выражению: 161Email=me@example.com 162if [[ "$Email" =~ [a-z]+@[a-z]{2,}\.(com|net|org) ]] 163then 164 echo "адрес корректный!" 165fi 166# Обратите внимание, что =~ работает только внутри 167# двойных квадратных скобок [[ ]], 168# которые несколько отличаются от одинарных скобок [ ]. 169# Для более подробной информации см. http://www.gnu.org/software/bash/manual/bashref.html#Conditional-Constructs. 170 171# Переопределить команду «ping» как псевдоним для отправки только пяти пакетов 172alias ping='ping -c 5' 173# Экранировать псевдоним и использовать команду под своим именем вместо него 174\ping 192.168.1.1 175# Вывести все псевдонимы 176alias -p 177 178# Выражения обозначаются таким форматом: 179echo $(( 10 + 5 )) # => 15 180 181# В отличие от других языков программирования, Bash — это командная оболочка, 182# а значит, работает в контексте текущей директории. 183# Вы можете просматривать файлы и директории в текущей директории командой ls: 184ls # перечисляет файлы и поддиректории в текущей директории 185 186# У этой команды есть параметры: 187ls -l # Показать каждый файл и директорию на отдельной строке 188ls -t # сортирует содержимое по дате последнего изменения (в обратном порядке) 189ls -R # Рекурсивно выполняет `ls` по данной директории и всем её поддиректориям 190 191# Результат предыдущей команды может быть направлен на вход следующей. 192# Команда grep фильтрует ввод по шаблону. 193# Так мы можем просмотреть только *.txt-файлы в текущей директории: 194ls -l | grep "\.txt" 195 196# Для вывода файлов в стандартный поток используйте `cat`: 197cat file.txt 198 199# С помощью `cat` мы также можем читать файлы: 200Contents=$(cat file.txt) 201echo "НАЧАЛО ФАЙЛА\n$Contents\nКОНЕЦ ФАЙЛА" # «\n» выводит символ перевода на новую строку 202# => НАЧАЛО ФАЙЛА 203# => [Содержимое file.txt] 204# => КОНЕЦ ФАЙЛА 205 206# Для копирования файлов и директорий из одного места в другое используйте `cp`. 207# `cp` создаёт новые версии исходных элементов, 208# так что редактирование копии не повлияет на оригинал (и наоборот). 209# Обратите внимание, что команда перезапишет целевой элемент, если он уже существует. 210cp srcFile.txt clone.txt 211cp -r srcDirectory/ dst/ # рекурсивное копирование 212 213# Если вам нужно обмениваться файлами между компьютерами, посмотрите в сторону `scp` или `sftp`. 214# `scp` ведёт себя очень похоже на `cp`. 215# `sftp` более интерактивна. 216 217# Для перемещения файлов и директорий из одного места в другое используйте `mv`. 218# Команда `mv` похожа на `cp`, но она удаляет исходный элемент. 219# `mv` также можно использовать для переименования файлов! 220mv s0urc3.txt dst.txt # Извините, тут были Leet-хакеры... 221 222# Поскольку Bash работает в контексте текущей директории, вам может понадобиться 223# запустить команду в другой директории. 224# Для изменения местоположения у нас есть `cd`: 225cd ~ # Перейти в домашнюю директорию 226cd # Также переходит в домашнюю директорию 227cd .. # Перейти на уровень вверх 228 # (например, из /home/username/Downloads в /home/username) 229cd /home/username/Documents # перейти в указанную директорию 230cd ~/Documents/.. # Всё ещё в домашней директории. Так ведь?? 231cd - # Перейти в последнюю директорию 232# => /home/username/Documents 233 234# Для работы по директориям используйте субоболочки 235(echo "Сначала я здесь: $PWD") && (cd someDir; echo "А теперь я тут: $PWD") 236pwd # всё ещё в первой директории 237 238# Для создания новых директорий используйте `mkdir`. 239mkdir myNewDir 240# Флаг `-p` указывает, что нужно создать все промежуточные директории, если нужно. 241mkdir -p myNewDir/with/intermediate/directories 242# Если промежуточные директории до этого не существовали, 243# вышеприведённая команда без флага `-p` вернёт ошибку 244 245# Вы можете перенаправить ввод и вывод команды (stdin, stdout и stderr). 246# Прочитать из stdin, пока не встретится ^EOF$, и 247# перезаписать hello.py следующими строками (до строки "EOF"): 248cat > hello.py << EOF 249#!/usr/bin/env python 250from __future__ import print_function 251import sys 252print("#stdout", file=sys.stdout) 253print("#stderr", file=sys.stderr) 254for line in sys.stdin: 255 print(line, file=sys.stdout) 256EOF 257# Если первый «EOF» не заключён в кавычки, переменные будут раскрыты 258 259# Запуск hello.py с разными вариантами перенаправления потоков 260# стандартных ввода, вывода и ошибок: 261python hello.py < "input.in" # передать input.in в качестве ввода в скрипт 262python hello.py > "output.out" # передать вывод скрипта в output.out 263python hello.py 2> "error.err" # передать вывод ошибок в error.err 264python hello.py > "output-and-error.log" 2>&1 # передать вывод скрипта и ошибок в output-and-error.log 265python hello.py > /dev/null 2>&1 # передать вывод скрипта и ошибок в «чёрную дыру» /dev/null, т.е., без вывода 266# Поток ошибок перезапишет файл, если этот файл существует, 267# поэтому, если вы хотите дописывать файл, используйте «>>»: 268python hello.py >> "output.out" 2>> "error.err" 269 270# Перезаписать output.txt, дописать error.err и сосчитать строки: 271info bash 'Basic Shell Features' 'Redirections' > output.out 2>> error.err 272wc -l output.out error.err 273 274# Запустить команду и вывести её файловый дескриптор (например, /dev/fd/123) 275# См. man fd 276echo <(echo "#helloworld") 277 278# Перезаписать output.txt строкой "#helloworld": 279cat > output.out <(echo "#helloworld") 280echo "#helloworld" > output.out 281echo "#helloworld" | cat > output.out 282echo "#helloworld" | tee output.out >/dev/null 283 284# Подчистить временные файлы с подробным выводом ('-i' — интерактивный режим) 285# ВНИМАНИЕ: команду `rm` отменить нельзя 286rm -v output.out error.err output-and-error.log 287rm -r tempDir/ # рекурсивное удаление 288 289# Команды могут быть подставлены в строку с помощью $( ): 290# следующие команды выводят число файлов и директорий в текущей директории. 291echo "Здесь $(ls | wc -l) элементов." 292 293# То же самое можно сделать с использованием обратных кавычек «``», 294# но они не могут быть вложенными, поэтому предпочтительно использовать $( ). 295echo "Здесь `ls | wc -l` элементов." 296 297# В Bash есть структура case, которая похожа на switch в Java и C++: 298case "$VARIABLE" in 299 # Перечислите шаблоны для условий, которые хотите выполнить 300 0) echo "Тут ноль.";; 301 1) echo "Тут один.";; 302 *) echo "Это не пустое значение.";; 303esac 304 305# Цикл for перебирает элементы по количеству аргументов: 306# Содержимое $VARIABLE будет напечатано три раза. 307for VARIABLE in {1..3} 308do 309 echo "$VARIABLE" 310done 311# => 1 312# => 2 313# => 3 314 315 316# Или с использованием «традиционного» синтаксиса цикла for: 317for ((a=1; a <= 3; a++)) 318do 319 echo $a 320done 321# => 1 322# => 2 323# => 3 324 325# Цикл for можно использовать для действий с файлами. 326# Запустим команду «cat» для файлов file1 и file2 327for VARIABLE in file1 file2 328do 329 cat "$VARIABLE" 330done 331 332# ... или выводом из команд 333# Запустим cat для вывода из ls. 334for OUTPUT in $(ls) 335do 336 cat "$OUTPUT" 337done 338 339# Цикл while: 340while [ true ] 341do 342 echo "Здесь тело цикла..." 343 break 344done 345# => Здесь тело цикла... 346 347# Вы также можете определять функции 348# Определение: 349function foo () 350{ 351 echo "Аргументы работают так же, как и аргументы скрипта: $@" 352 echo "И так: $1 $2..." 353 echo "Это функция" 354 return 0 355} 356# Вызовем функцию `foo` с двумя аргументами, arg1 и arg2: 357foo arg1 arg2 358# => Аргументы работают так же, как и аргументы скрипта: arg1 arg2 359# => И так: arg1 arg2... 360# => Это функция 361 362# или просто 363bar () 364{ 365 echo "Другой способ определять функции!" 366 return 0 367} 368# Вызовем функцию `bar` без аргументов: 369bar # => Другой способ определять функции! 370 371# Вызов функции 372foo "Меня зовут" $NAME 373 374# Есть много полезных команд, которые нужно знать: 375# напечатать последние 10 строк файла file.txt 376tail -n 10 file.txt 377 378# напечатать первые 10 строк файла file.txt 379head -n 10 file.txt 380 381# отсортировать строки file.txt 382sort file.txt 383 384# отобрать или наоборот пропустить повторяющиеся строки (с параметром `-d` отбирает строки) 385uniq -d file.txt 386 387# напечатать только первый столбец перед символом «,» 388cut -d ',' -f 1 file.txt 389 390# заменить каждое вхождение «хорошо» на «прекрасно» в файле file.txt 391# (поддерживаются регулярные выражения) 392sed -i 's/хорошо/прекрасно/g' file.txt 393 394# вывести в stdout все строки из file.txt, соответствующие регулярному выражению 395# этот пример выводит строки, которые начинаются на «foo» и оканчиваются на «bar» 396grep "^foo.*bar$" file.txt 397 398# Передайте параметр `-c`, чтобы вывести лишь число строк, 399# соответствующих регулярному выражению 400grep -c "^foo.*bar$" file.txt 401 402# Ниже приведены другие полезные параметры: 403grep -r "^foo.*bar$" someDir/ # рекурсивный `grep` 404grep -n "^foo.*bar$" file.txt # задаются номера строк 405grep -rI "^foo.*bar$" someDir/ # рекурсивный `grep` с игнорированием двоичных файлов 406 407# Выполнить тот же изначальный поиск, но удалив строки, содержащие «baz» 408grep "^foo.*bar$" file.txt | grep -v "baz" 409 410# чтобы искать непосредственно по строке, а не в соответствии 411# с регулярным выражением, используйте fgrep (или grep -F): 412fgrep "^foo.*bar$" file.txt 413 414# Команда `trap` позволяет выполнить некую команду, когда ваш скрипт 415# принимает определённый Posix-сигнал. В следующем примере `trap` выполнит `rm`, 416# если скрипт примет один из трёх перечисленных сигналов. 417trap "rm $TEMP_FILE; exit" SIGHUP SIGINT SIGTERM 418 419# `sudo` используется для выполнения команд с правами суперпользователя 420NAME1=$(whoami) 421NAME2=$(sudo whoami) 422echo "Был $NAME1, затем стал более мощным $NAME2" 423 424# Читайте встроенную документацию оболочки Bash командой `help`: 425help 426help help 427help for 428help return 429help source 430help . 431 432# Читайте man-документацию Bash командой `man`: 433apropos bash 434man 1 bash 435man bash 436 437# Читайте документацию info (? для справки) 438apropos info | grep '^info.*(' 439man info 440info info 441info 5 info 442 443# Читайте info-документацию Bash: 444info bash 445info bash 'Bash Features' 446info bash 6 447info --apropos bash