Об этой серии. Присвоение значений переменным в конвейере

Данный топик является четвертым топиком цикла «Язык командного интерпретатора bash». Он будет повествовать о таких управляющих структурах языка, как условные операторы. Но перед тем, как перейти к их описанию, необходимо остановиться на некоторых нюансах, которые сделают рассмотрение нижеизложенного материала более понятным.
Во-первых, разберём, что такое список команд. Список команд – это одиночная команда, конвейер или последовательность команд/конвейеров, разделённых одним из следующих операторов: ";", "&&", "||", завершённая точкой с запятой.
; - оператор последовательного выполнения нескольких команд. Каждая последующая команда начинает выполняться только после завершения предыдущей (неважно, успешного или нет);
&& - оператор выполнения команды только после успешного выполнения предыдущей;
|| - оператор выполнения команды только после ошибочного выполнения предыдущей.
Кодом успешного завершения является 0, а ошибочного - не ноль (зависит от типа ошибки). Не нужно путать с обычными языками программирования, когда 1 является аналогом true, а 0 – false.
Теперь можно приступить к непосредственному рассмотрению условных операторов.

Оператор вариантов case

Общий синтаксис оператора case:

case значение in
шаблон1) список1;;
шаблон2 | шаблон3) список2;;
esac

Логическая последовательность выполнения оператора case:
а) ищется первый шаблон, совпадающий со значением;
б) если он найден, выполняется соответствующий ему список команд, завершённый ";;";
в) управление передаётся операторам, следующим за конструкцией case.
Шаблон и список разделяются символом ")". Одному списку команд может соответствовать несколько условий, тогда их нужно разделять символом "|".
В шаблонах можно использовать символы "*", "?", "", о которых было рассказано во втором топике цикла. С их помощью можно реализовать инструкцию, действующую как default в операторе switch таких языков, как C, PHP.
Приведу пример использования case:
echo -n "[Универсальный просмоторщик] Укажите имя файла: "; read File case "$File" in *.jpg|*.gif|*.png) eog $File ;; *.pdf) evince $File ;; *.txt) less $File ;; *.html) firefox $File ;; /dev/*) echo "Ну это страшные файлы." ;; *) echo "Ну ладно, ладно - не такой уж и универсальный." echo "Этот тип файлов мне не знаком. Не знаю, чем его просмотреть." ;; esac
Ещё один пример использования конструкции case:
echo "Ошибка. Кому отправить сообщение?" echo "Начальнику: b" echo "Коллегам: c" echo "Никому: any key" read answer case $answer in b|B) mail –s "error log" boss < error.log;; c|C) mail –s "Help! error log" –c denis nick < error.log;; *) echo "error"; exit;; esac

Условный оператор if

Общий синтаксис оператора if:

if список1 then
список2

fi

Квадратные скобки здесь указывают на необязательные конструкции. Логическая последовательность выполнения оператора case:
а) выполняется список1;
б) если он выполнен без ошибок, выполняется список2. В противном случае выполняется список3, и если он завершается без ошибок – список4. Если же и список3 возвращает код ошибки, выполняется список5;
в) управление передаётся операторам, следующим за конструкцией if.
Приведу пример использования if:
if grep -q Bash file then echo "Файл содержит, как минимум, одно слово Bash." fi
Когда if и then располагаются в одной строке, то конструкции if и then должны завершаться точкой с запятой. Например:
$ if [ $? –ne 0 ]; then echo “Error”; fi
Теперь, зная о возможни располагать if и then в одной строке, перепишем вышеуказанный пример:
if grep -q Bash file; then echo «Файл содержит слово Bash.» fi

Оператор test и условные выражения

В вышеприведённом примере вместо анализа кода завершения использована проверка условия. Две формы такой проверки эквивалентны: встроенная команда test и [условие]. Например, для проверки существования файла нужно написать:
test –e <файл>
или
[ -e <файл> ]
Если используются квадратные скобки, они обязательно должны быть отделены друг от друга пробелом, потому что "[" – это название команды, а "]" – это обязательный последний аргумент её завершения.
В случае успешной проверки условия, возвращается 0, а в случае ложности – код ошибки 1.
Команда test может проверять строку на пустоту. Непустая строка приводит к коду завершения 0. Пуста, соответственно – 1. Например:
$ test $USER; echo $? 0
Конструкция "" более универсальна, по сравнению с "". Этот расширенный вариант команды test. Внутри этой конструкции не производится никакой дополнительной интерпретации имен файлов и не производится разбиение аргументов на отдельные слова, но допускается подстановка параметров и команд. Например:
file=/etc/passwd if [[ -e $file ]] then echo “Файл паролей найден.” fi
Конструкция "" более предпочтительна, нежели "", поскольку поможет избежать некоторых логических ошибок. Например, операторы "&&", "||", "<" и ">" внутри "" вполне допустимы, в то время как внутри "" порождают сообщения об ошибках.
Конструкция "(())" позволяет производить вычисление арифметических выражений внутри неё. Если результатом вычислений является ноль, то возвращается код ошибки. Ненулевой результат вычислений даёт код возврата 0. То есть полная противоположность инструкциям test и "", обсуждавшимся выше.
Оператор if позволяет допускать наличие вложенных проверок:
if echo "Следующий *if* находится внутри первого *if*." if [[ $comparison = "integer" ]] then ((a < b)) else [[ $a < $b ]] fi then echo "$a меньше $b" fi

Условные выражения можно комбинировать с помощью обычных логических операций:
! <выражение> – отрицание;
<выражение1> –a <выражение2> – логическое И;
<выражение1> –o <выражение2> – логическое ИЛИ.

Элементарные условные выражения для файлов:
-e - файл существует;
-f - обычный файл (не каталог и не файл устройства);
-s - ненулевой размер файла;
-d - файл является каталогом;
-b - файл является блочным устройством (floppy, cdrom и т.п.);
-c - файл является символьным устройством (клавиатура, модем, звуковая карта и т.п.);
-p - файл является каналом;
-h - файл является символической ссылкой;
-L - файл является символической ссылкой;
-S - файл является сокетом;
-t - файл связан с терминальным устройством;
-r - файл доступен для чтения (пользователю, запустившему сценарий);
-w - файл доступен для записи (пользователю, запустившему сценарий);
-x - файл доступен для исполнения (пользователю, запустившему сценарий);
-g - (sgid) флаг для файла или каталога установлен;
-u - (suid) флаг для файла установлен;
-k - флаг sticky bit установлен;
-O - вы являетесь владельцем файла;
-G - вы принадлежите к той же группе, что и файл;
-N - файл был модифицирован с момента последнего чтения;
файл1 -nt файл2 – файл1 более новый, чем файл2;
файл1 -ot файл2 – файл1 более старый, чем файл2;
файл1 -ef файл2 – файл1 и файл2 являются «жесткими» ссылками на один и тот же файл.

Элементарные условные выражение для сравнения строк:
-z строка – длина строки равна 0;
-n строка – длина строки не равно 0;
строка1 == строка2 – строки совпадают (аналог “=”);
строка1 !== строка2 – строки не совпадают (аналог “!=”);
строка1 < строка2 – строка1 предшествует строке2 в лексикографическом порядке;
строка1 > строка2 – строка1 следует за строкой2 в лексикографическом порядке.
Арифметическое условное выражение имеет формат:
аргумент1 операция аргумент2, где аргументами являются целые числа, и допустимы следующие операции:
-eq – равно;
-ne – не равно;
-lt – меньше;
-le – меньше или равно;
-gt – больше;
-ge – больше или равно;
< - меньше (внутри двойных круглых скобок);
<= - меньше или равно (внутри двойных круглых скобок);
> - больше (внутри двойных круглых скобок);
>= - больше или равно (внутри двойных круглых скобок).

Перепишем предыдущий пример с использованием оператора if:
echo "Ошибка. Кому отправить сообщение?" echo "Начальнику: b" echo "Коллегам: c" echo "Никому: any key" read answer if [ "$answer" == "b" –o "$answer" == "B" ]; then mail –s "error log" boss < error.log; elif [ "$answer" == "c" –o "$answer" == "C" ]; then mail –s "Help! error log" –c denis nick < error.log; else echo "error"; exit; fi

В следующем топике я продолжу рассматривать управляющие структуры командного интерпретатора bash. А именно, будут рассмотрены операторы циклов. А сейчас жду комментариев и критики:).

UPD : Спасибо пользователю

Изучаем Linux, 101

Потоки, программные каналы и перенаправления

Изучение основ работы с конвейерами Linux

Серия контента:

Краткий обзор

Из этой статьи вы узнаете об основных приемах перенаправления стандартных потоков ввода/вывода в Linux. Вы научитесь:

  • Перенаправлять стандартные потоки ввода/вывода: стандартный поток ввода, стандартный поток вывода и стандартный поток ошибок.
  • Направлять вывод одной команды на вход другой команды.
  • Отправлять вывод одновременно на стандартное устройство вывода (stdout) и в файл.
  • Использовать вывод команды в качестве аргументов другой команды.

Эта статья поможет вам подготовиться к сдаче экзамена LPI 101 на администратора начального уровня (LPIC-1) и содержит материалы цели 103.4 темы 103. Цель имеет вес 4.

Об этой серии

Эта серия статей поможет вам освоить задачи администрирования операционной системы Linux. Вы также можете использовать материал этих статей для подготовки к .

Чтобы посмотреть описания статей этой серии и получить ссылки на них, обратитесь к нашему . Этот перечень постоянно дополняется новыми статьями по мере их готовности и содержит самые последние (по состоянию на апрель 2009 года) цели экзаменов сертификации LPIC-1. Если какая-либо статья отсутствует в перечне, можно найти ее более раннюю версию, соответствующую предыдущим целям LPIC-1 (до апреля 2009 года), обратившись к нашим .

Необходимые условия

Чтобы извлечь наибольшую пользу из наших статей, необходимо обладать базовыми знаниями о Linux и иметь работоспособный компьютер с Linux, на котором можно будет выполнять все встречающиеся команды. Иногда различные версии программ выводят результаты по-разному, поэтому содержимое листингов и рисунков может отличаться от того, что вы увидите на вашем компьютере.

Подготовка к выполнению примеров

Как связаться с Яном

Ян – один из наших наиболее популярных и плодовитых авторов. Ознакомьтесь со (EN), опубликованными на сайте developerWorks. Вы можете найти контактные данные в и связаться с ним, а также с другими авторами и участниками ресурса My developerWorks.

Для выполнения примеров в этой статье мы будем использовать некоторые файлы, созданные ранее в статье " ". Если вы не читали эту статью или не сохранили эти файлы, не расстраивайтесь! Давайте начнем с создания новой директории lpi103-4 и всех необходимых файлов. Для этого откройте текстовое окно и перейдите в вашу домашнюю директорию. Скопируйте содержимое листинга 1 в текстовое окно; в результате выполнения команд в вашей домашней директории будет создана поддиректория lpi103-4 и все необходимые файлы в ней, которые мы и будем использовать в наших примерах.

Листинг 1. Создание файлов, необходимых для примеров этой статьи
mkdir -p lpi103-4 && cd lpi103-4 && { echo -e "1 apple\n2 pear\n3 banana" > text1 echo -e "9\tplum\n3\tbanana\n10\tapple" > text2 echo "This is a sentence. " !#:* !#:1->text3 split -l 2 text1 split -b 17 text2 y; }

Ваше окно должно выглядеть так, как показано в листинге 2, а вашей текущей рабочей директорией должна стать вновь созданная директория lpi103-4.

Листинг 2. Результаты создания необходимых файлов
$ mkdir -p lpi103-4 && cd lpi103-4 && { > echo -e "1 apple\n2 pear\n3 banana" > text1 > echo -e "9\tplum\n3\tbanana\n10\tapple" > text2 > echo "This is a sentence. " !#:* !#:1->text3echo "This is a sentence. " "This is a sentence. " "This is a sentence.">text3 > split -l 2 text1 > split -b 17 text2 y; } $

Перенаправление стандартного ввода/вывода

Командный интерпретатор Linux, такой как Bash, получает входные данные и направляет выходные данные в виде последовательностей или потоков символов. Любой символ не зависит ни от предыдущих, ни от последующих символов. Символы не упорядочены в виде структурированных записей или блоков с фиксированным размером. Доступ к потокам осуществляется с помощью механизмов ввода/вывода независимо от того, откуда поступают и куда передаются потоки символов (файл, клавиатура, окно, экран или другое устройство ввода/вывода). Командные интерпретаторы Linux используют три стандартных потока ввода/вывода, каждому из которых назначен определенный файловый дескриптор.

  1. stdout стандартный поток вывода , отображает вывод команд и имеет дескриптор 1.
  2. stderr стандартный поток ошибок , отображает ошибки команд и имеет дескриптор 2.
  3. stdin стандартный поток ввода , передает входные данные командам и имеет дескриптор 0.

Потоки ввода обеспечивают передачу входных данных (обычно поступающих с клавиатуры) командам. Потоки вывода обеспечивают печать текстовых символов, как правило, на терминал. Изначально терминал представлял собой ASCII печатающее устройство или дисплейный терминал, но сейчас, как правило, это просто окно на рабочем столе компьютера.

Если вы уже прочитали руководство " ", то часть материала из этой статьи окажется вам знакомой.

Перенаправление вывода

Существует два способа перенаправления вывода в файл:

n > перенаправляет вывод из файлового дескриптора n в файл. Вы должны иметь права на запись в файл. Если файла не существует, то он будет создан. Если файл существует, то все его содержимое, как правило, уничтожается без каких-либо предупреждений. n >> также перенаправляет вывод из файлового дескриптора n в файл. Вы также должны иметь права на запись в файл. Если файла не существует, то он будет создан. Если файл существует, то вывод добавляется к его содержимому.

Символ n в операторах n> или n>> является файловым дескриптором . Если он не указан, то предполагается, что используется стандартное устройство вывода. В листинге 3 продемонстрирована операция перенаправления для разделения стандартного потока вывода и стандартного потока ошибок команды ls с использованием файлов, которые были созданы ранее в директории lpi103-4. Также продемонстрировано добавление вывода команды в существующие файлы.

Листинг 3. Перенаправление вывода
$ ls x* z* ls: cannot access z*: No such file or directory xaa xab $ ls x* z* >stdout.txt 2>stderr.txt $ ls w* y* ls: cannot access w*: No such file or directory yaa yab $ ls w* y* >>stdout.txt 2>>stderr.txt $ cat stdout.txt xaa xab yaa yab $ cat stderr.txt ls: cannot access z*: No such file or directory ls: cannot access w*: No such file or directory

Мы уже говорили, что перенаправление вывода с помощью оператора n> обычно приводит к перезаписи существующих файлов. Вы можете управлять этим свойством при помощи опции noclobber встроенной команды set . Если эта опция определена, то вы можете переопределить ее с помощью оператора n>|, как показано в листинге 4.

Листинг 4. Перенаправление вывода с помощью опции noclobber
$ set -o noclobber $ ls x* z* >stdout.txt 2>stderr.txt -bash: stdout.txt: cannot overwrite existing file $ ls x* z* >|stdout.txt 2>|stderr.txt $ cat stdout.txt xaa xab $ cat stderr.txt ls: cannot access z*: No such file or directory $ set +o noclobber #restore original noclobber setting

Иногда может потребоваться перенаправить в файл как стандартный вывод, так и стандартный поток ошибок. Часто это используется в автоматизированных процессах или фоновых заданиях для того, чтобы впоследствии можно было просмотреть результаты работы. Чтобы перенаправить стандартный вывод и стандартный поток ошибок в одно и то же место, используйте оператор &> или &>>. Альтернативный вариант – перенаправить файловый дескриптор n и затем файловый дескриптор m в одно и то же место с помощью конструкции m>&n или m>>&n. В этом случае важен порядок перенаправления потоков. Например, команда
command 2>&1 >output.txt
это не то же самое, что команда
command >output.txt 2>&1
В первом случае поток ошибок stderr перенаправляется в текущее месторасположение потока stdout, а затем поток stdout перенаправляется в файл output.txt; однако второе перенаправление затрагивает только stdout, но не stderr. Во втором случае поток stderr перенаправляется в текущее месторасположение потока stdout, то есть, в файл output.txt. Эти перенаправления проиллюстрированы в листинге 5. Обратите внимание на последнюю команду, в которой стандартный вывод был перенаправлен после стандартного потока ошибок, и, как следствие, поток ошибок продолжает выводиться в окно терминала.

Листинг 5. Перенаправление двух потоков в один файл
$ ls x* z* &>output.txt $ cat output.txt ls: cannot access z*: No such file or directory xaa xab $ ls x* z* >output.txt 2>&1 $ cat output.txt ls: cannot access z*: No such file or directory xaa xab $ ls x* z* 2>&1 >output.txt # stderr does not go to output.txt ls: cannot access z*: No such file or directory $ cat output.txt xaa xab

В других ситуациях вам может потребоваться полностью проигнорировать стандартный вывод или стандартный поток ошибок. Для этого следует перенаправить соответствующий поток в пустой файл /dev/null. В листинге 6 показано, как проигнорировать поток ошибок команды ls и как с помощью команды cat убедиться в том, что файл /dev/null на самом деле пуст.

Листинг 6. Игнорирование стандартного потока ошибок посредством использования /dev/null
$ ls x* z* 2>/dev/null xaa xab $ cat /dev/null

Перенаправление ввода

Так же, как мы можем перенаправить потоки stdout и stderr, мы можем перенаправить поток stdin из файла с помощью оператора <. Если вы прочли руководство " ", то должны помнить, что в разделе была использована команда tr для замены пробелов в файле text1 на символы табуляции. В том примере мы использовали вывод команды cat чтобы создать стандартный поток ввода для команды tr . Теперь для преобразования пробелов в символы табуляции вместо бесполезного вызова команды cat мы можем использовать перенаправление ввода, как показано в листинге 7.

Листинг 7. Перенаправление ввода
$ tr " " "\t"

В командных интерпретаторах, в том числе и в bash, реализована концепция here-document , которая является одним из способов перенаправления ввода. В ней используется конструкция << и какое-либо слово, например END, являющееся маркером, или сигнальной меткой, означающей конец ввода. Эта концепция продемонстрирована в листинге 8.

Листинг 8. Перенаправление ввода с использованием концепции here-document
$ sort -k2 < 1 apple > 2 pear > 3 banana > END 1 apple 3 banana 2 pear

Но почему нельзя просто набрать команду sort -k2 , ввести данные и нажать комбинацию Ctrl-d , означающую конец ввода? Разумеется, вы могли бы выполнить эту команду, но тогда вы не узнали бы о концепции here-document, которая очень часто используется в сценариях командной оболочки (в которых не существует другой возможности указать, какие именно строки должны восприниматься в качестве ввода). Поскольку для выравнивания текста и обеспечения удобства чтения в сценариях широко используются символы табуляции, существует другой прием использования концепции here-document. При использовании оператора <<- вместо оператора << начальные символы табуляции удаляются.

В листинге 9 мы использовали подстановку команд для создания символа табуляции, а затем создали небольшой сценарий командной оболочки, содержащий две команды cat , каждая из которых считывает данные из блока here-document. Заметьте, что мы использовали слово END в качестве сигнальной метки блока here-document, который мы считываем с терминала. Если бы мы использовали это же слово в нашем сценарии, то наш ввод закончился бы преждевременно. Поэтому вместо слова END мы используем в сценарии слово EOF. После того, как наш сценарий создан, мы используем команду. (точка) чтобы запустить его в контексте текущего командного интерпретатора.

Листинг 9. Перенаправление ввода с использованием концепции here-document
$ ht=$(echo -en "\t") $ cat<ex-here.sh > cat <<-EOF > apple > EOF > ${ht}cat <<-EOF > ${ht}pear > ${ht}EOF > END $ cat ex-here.sh cat <<-EOF apple EOF cat <<-EOF pear EOF $ . ex-here.sh apple pear

В следующих статях этой серии вы узнаете больше о подстановке команд и сценариях. Ссылки на все статьи этой серии вы можете найти в .

Создание конвейеров

Использование команды xargs

Команда xargs считывает данные со стандартного устройства ввода, а затем строит и выполняет команды, параметрами которых являются полученные входные данные. Если не указана никакая команда, то используется команда echo . В листинге 12 приведен простой пример использования нашего файла text1, содержащего три строки по два слова в каждой.

Листинг 12. Использование команды xargs
$ cat text1 1 apple 2 pear 3 banana $ xargs

Почему же тогда вывод xargs содержит только одну строку? По умолчанию xargs разбивает входные данные, если встречает символы-разделители, и каждый полученный фрагмент становится отдельным параметром. Однако когда xargs строит команду, ей за один раз передается максимально возможное количество параметров. Это поведение можно изменить с помощью параметра –n или --max-args . В листинге 13 приведен пример использования обоих вариантов; также был выполнен явный вызов команды echo для использования с xargs .

Листинг 13. Использование команд xargs и echo
$ xargs" args > 1 apple 2 pear 3 banana $ xargs --max-args 3 " args > 1 apple 2 args > pear 3 banana $ xargs -n 1 " args > 1 args > apple args > 2 args > pear args > 3 args > banana

Если входные данные содержат пробелы, но при этом они заключены в одиночные или двойные кавычки (либо пробелы представлены в виде escape-последовательностей с использованием обратной косой черты), то xargs не будет разбивать их на отдельные части. Это показано в листинге 14.

Листинг 14. Использование команды xargs и кавычек
$ echo ""4 plum"" | cat text1 - 1 apple 2 pear 3 banana "4 plum" $ echo ""4 plum"" | cat text1 - | xargs -n 1 1 apple 2 pear 3 banana 4 plum

До сих пор все аргументы добавлялись в конец команды. Если вам необходимо, чтобы после них были добавлены другие дополнительные аргументы, то воспользуйтесь опцией -I для указания строки замещения. В том месте вызываемой через xargs команды, в котором используется строка замещения, вместо нее будет подставлен аргумент. При использовании такого подхода каждой команде передается только один аргумент. Однако аргумент будет создан из целой входной строки, а не из отдельного ее фрагмента. Также вы можете использовать опцию -L команды xargs , в результате чего в качестве аргумента будет использоваться вся строка целиком, а не отдельные ее фрагменты, разделенные пробелами. Использование опции -I неявно вызывает использование опции -L 1 . В листинге 15 приведены примеры использования опций -I и –L .

Листинг 15. Использование команды xargs и строк ввода
$ xargs -I XYZ echo "START XYZ REPEAT XYZ END" " <9 plum> <3 banana><3 banana> <10 apple><10 apple> $ cat text1 text2 | xargs -L2 1 apple 2 pear 3 banana 9 plum 3 banana 10 apple

Хотя в наших примерах используются простые текстовые файлы, вы не будете часто использовать команду xargs для таких случаев. Как правило, вы будете иметь дело с большим списком файлов, полученных в результате выполнения таких команд, как ls , find или grep . В листинге 16 показан один из способов передачи через xargs списка содержимого директории такой команде, как, например, grep .

Листинг 16. Использование команды xargs и списка файлов
$ ls |xargs grep "1" text1:1 apple text2:10 apple xaa:1 apple yaa:1

Что произойдет в последнем примере, если одно или несколько имен файлов будут содержать пробелы? Если вы попытаетесь использовать команду так, как это было сделано в листинге 16, то вы получите ошибку. В реальной ситуации список файлов может быть получен не от команды ls , а, например, в результате выполнения пользовательского сценария или команды; а может быть, вы захотите обработать его на других этапах конвейера с целью дополнительной фильтрации. Поэтому мы не берем во внимание тот факт, что вы могли бы просто использовать команду grep "1" * вместо существующей логической структуры.

В случае с командой ls вы могли бы использовать опцию --quoting-style для того, чтобы имена файлов, содержащие пробелы, были заключены в скобки (или представлены в виде escape-последовательностей). Лучшим решением (когда это возможно) является использование опции -0 команды xargs , в результате чего для разделения входных аргументов используются пустые символы (\0). Хотя команда ls не имеет опции, позволяющей использовать в качестве вывода имена файлов с завершающим нулем, многие команды умеют делать это.

В листинге 17 мы сначала скопируем файл text1 в "text 1", а затем приведем несколько примеров использования списка имен файлов, содержащих пробелы, с командой xargs . Эти примеры позволяют понять саму идею, поскольку полностью освоить работу с xargs может оказаться не так просто. В частности, последний пример преобразования символов новой строки в пустые символы не сработал бы в том случае, если некоторые имена файлов уже содержали бы символы новой строки. В следующем разделе этой статьи мы рассмотрим более надежное решение с применением команды find для генерации подходящего вывода, в котором в качестве разделителей используются пустые символы.

Листинг 17. Использование команды xargs и файлов, содержащих пробелы в именах
$ cp text1 "text 1" $ ls *1 |xargs grep "1" # error text1:1 apple grep: text: No such file or directory grep: 1: No such file or directory $ ls --quoting-style escape *1 text1 text\ 1 $ ls --quoting-style shell *1 text1 "text 1" $ ls --quoting-style shell *1 |xargs grep "1" text1:1 apple text 1:1 apple $ # Illustrate -0 option of xargs $ ls *1 | tr "\n" "\0" |xargs -0 grep "1" text1:1 apple text 1:1 apple

Команда xargs не может строить сколь угодно длинные команды. Так, в Linux до версии ядра 2.26.3 максимальная длина команды была ограничена. Если вы попытаетесь выполнить такую команду, как, например, rm somepath/* , а директория содержит множество файлов с длинными именами, то выполнение может завершиться ошибкой, сообщающей, что список аргументов слишком длинный. Если вы работаете с более старыми версиями Linux или UNIX, в которых могут присутствовать такие ограничения, то будет полезно узнать, как можно использовать xargs таким образом, чтобы обойти их.

Вы можете использовать опцию --show-limits для просмотра ограничений, установленных по умолчанию для команды xargs , и опцию -s – для задания максимальной длины выводимых команд. Об остальных опциях вы можете узнать из man-страниц.

Использование команды find с опцией -exec или совместно с командой xargs

Из руководства " " вы узнали о том, как использовать команду find для поиска файлов на основе их имен, времени модификации, размера и прочих характеристик. Обычно над найденными файлами необходимо выполнять определенные действия – удалять, копировать, переименовывать их и так далее. Сейчас мы рассмотрим опцию -exec команды find , работа которой похожа на работу команды find с последующей передачей вывода команде xargs .

Листинг 18. Использование команды find с опцией -exec
$ find text -exec cat text3 {} \; This is a sentence. This is a sentence. This is a sentence. 1 apple 2 pear 3 banana This is a sentence. This is a sentence. This is a sentence. 9 plum 3 banana 10 apple

Сопоставив результаты листинга 18 с тем, что вам уже известно о xargs , можно обнаружить несколько различий.

  1. Вы должны использовать в команде символы {} для указания места подстановки, в которое будет подставлено имя файла. Эти символы не добавляются автоматически в конце команды.
  2. Вы должны завершить команду точкой с запятой, которая должна быть представлена в виде escape-последовательности (\;, ";" или ";").
  3. Команда выполняется один раз для каждого входного файла.

Попробуйте самостоятельно выполнить команду find text |xargs cat text3 , чтобы увидеть различия.

Теперь давайте вернемся к случаю, когда имя файла содержит пробелы. В листинге 19 мы попытались использовать команду find с опцией -exec вместо команд ls и xargs .

Листинг 19. Использование команды find с опцией -exec и файлов, содержащих пробелы в именах
$ find . -name "*1" -exec grep "1" {} \; 1 apple 1 apple

Пока все хорошо. Однако не кажется ли вам, что чего-то здесь не хватает? В каких файлах присутствовали строки, найденные при помощи grep ? Здесь не хватает имен файлов, поскольку find вызывает grep один раз для каждого файла, а grep , будучи умной командой, знает о том, что если ей было передано имя лишь одного файла, то нет необходимости сообщать вам о том, что это был за файл.

В этой ситуации мы могли бы воспользоваться командой xargs , однако мы уже знаем о проблеме с файлами, имена которых содержат пробелы. Также мы упоминали тот факт, что команда find может генерировать список имен с пустыми разделителями, благодаря опции -print0 . Современные версии команды find могут разделяться не точкой с запятой, а знаком +, благодаря чему, за один вызов команды find можно передать максимально возможное число имен, так же, как и в случае использования xargs . Излишне говорить о том, что в этом случае вы можете использовать конструкцию {} только один раз, и что она должна являться последним параметром команды. В листинге 20 продемонстрированы оба этих метода.

Листинг 20. Использование команд find , xargs и файлов, содержащих пробелы в именах
$ find . -name "*1" -print0 |xargs -0 grep "1" ./text 1:1 apple ./text1:1 apple $ find . -name "*1" -exec grep "1" {} + ./text 1:1 apple ./text1:1 apple

Оба этих метода являются рабочими и выбор какого-то одного из них часто обусловлен лишь личными предпочтениями пользователя. Помните о том, что передавая по конвейеру объекты с необработанными символами-разделителями и пробелами, вы можете столкнуться с проблемами; поэтому если вы передаете вывод команде xargs , то используйте опцию -print0 команды find , а также опцию -0 команды xargs , которая сообщает, что во входных данных используются пустые разделители. Другие команды, включая tar , также поддерживают опцию -0 и работу с входными данными, содержащими пустые разделители, поэтому всегда следует использовать эту опцию для тех команд, которые ее поддерживают, если только вы уверены на все 100%, что входной список не создаст вам проблем.

Наш последний комментарий затрагивает работу со списком файлов. Хорошей идеей будет всегда тщательно проверять ваш список и команды, прежде чем выполнять пакетные операции (например, удаление или переименование множества файлов). Наличие актуальной резервной копии в нужный момент также может оказаться неоценимым.

Разделение вывода

В завершение этой статьи мы кратко рассмотрим еще одну команду. Иногда может возникнуть необходимость просматривать вывод на экране и одновременно сохранять его в файл. Для этого вы могли бы перенаправить вывод команды в файл в одном окне, а затем с помощью tail -fn1 отслеживать вывод в другом окне, однако проще всего использовать команду tee .

Команда tee используется в конвейере, а ее аргументом является имя файла (или имена нескольких файлов), в который будет передаваться стандартный вывод. Опция -a позволяет не замещать старое содержимое файла новым содержимым, а добавлять данные в конец файла. Как уже говорилось при рассмотрении конвейеризации, если вы хотите сохранить как стандартный вывод, так и поток ошибок, то необходимо перенаправлять поток stderr в поток stdout прежде, чем передавать данные на вход команде tee . В листинге 21 приведен пример использования команды tee для сохранения вывода в два файла, f1 и f2.

Листинг 21. Разделение потока stdout с помощью команды tee
$ ls text|tee f1 f2 text1 text2 text3 $ cat f1 text1 text2 text3 $ cat f2 text1 text2 text3

Что требуется?

Необходимо ознакомиться с командной строкой, а также с основными концепциями программирования. Несмотря на то, что это не является учебником по программированию, здесь объясняются (или, по крайней мере, осуществляется попытка объяснить) многие основные концепции.
Использование данного документа

Данный документ может быть необходим в следующих ситуациях:

У Вас имеются идеи, связанные с программированием, и существует необходимость в выполнении процесса кодирования каких-нибудь shell-скриптов.

Ваши идеи, связанные с программированием, недостаточно конкретны и требуют дополнительных указаний.

Вы желаете взглянуть на какие-нибудь shell-скрипты и комментарии в качестве образца для создания своих собственных.

Вы мигрируете из DOS/Windows (или уже выполнили это) и хотите создавать файлы пакетной обработки ("batch").

Вы - полный нерд и читаете любой попавший под руку how-to.

Простейшие скрипты

В данном HOW-TO осуществляется попытка предоставить Вам некоторые рекомендации по shell-программированию, основанные только на примерах.

В данном разделе Вы обнаружите небольшие скрипты, которые, вероятно, будут Вам полезны при освоении некоторых приёмов.

Традиционный скрипт "hello world"

#!/bin/bash echo Hello World!

Данный скрипт содержит только две строки. Первая сообщает системе о том, какая программа используется для запуска файла.

Вторая строка - это единственное действие, выполняемое данным скриптом, печатающее "Hello world" на терминале.

Если Вы получите что-то типа./hello.sh: Command not found. , то, возможно, первая строка "#!/bin/bash" неправильная; запустите whereis bash или посмотрите finding bash, чтобы выяснить, какой должна быть эта строка.

Простой скрипт резервного копирования

#!/bin/bash
tar -cZf /var/my-backup.tgz /home/me/

В данном скрипте вместо печати сообщения на терминале мы создаём tar-архив пользовательского домашнего каталога. Скрипт НЕ предназначен для практического применения. Далее в данном документе будет представлен более эффективный скрипт резервного копирования.

Всё о перенаправлении Теория и быстрый просмотр

Существуют 3 файловых дескриптора: stdin - cтандартный ввод, stdout - стандартный вывод и stderr - стандартный поток ошибок.

Ваши основные возможности:
перенаправлять stdout в файл
перенаправлять stderr в файл
перенаправлять stdout в stderr
перенаправлять stderr в stdout
перенаправлять stderr и stdout в файл
перенаправлять stderr и stdout в stdout
перенаправлять stderr и stdout в stderr

1 означает stdout, а 2 - stderr. Небольшое примечание для более глубокого понимания: с помощью команды less Вы можете просмотреть как stdout, который остаётся в буфере, так и stderr, который печатается на экране. Однако он стирается, когда Вы предпринимаете попытки "просмотреть" буфер.

Пример: stdout в файл

Это действие записывает стандартный вывод программы в файл.

ls -l > ls-l.txt

Здесь создаётся файл с именем "ls-l.txt". В нём будет содержаться всё то, что Вы бы увидели, если бы просто выполнили команду "ls -l". 3.3 Пример: stderr в файл
Это действие записывает стандартный поток ошибок программы в файл.
grep da * 2> grep-errors.txt

Здесь создаётся файл, названный "grep-errors.txt". В нём будет содержаться часть вывода команды "grep;da;*", относящаяся к стандартному потоку ошибок. 3.4 Пример: stdout в stderr

Это действие записывает стандартный вывод программы в тот же файл, что и стандартный поток ошибок.
grep da * 1>&2

Здесь стандартный вывод команды отправляется в стандартный поток ошибок. Вы можете увидеть это разными способами. 3.5 Sample: stderr 2 stdout

Это действие записывает стандартный поток ошибок программы туда же, куда и стандартный вывод.
grep * 2>&1
Здесь стандартный поток ошибок команды отправляется на стандартный вывод; если Вы перешлёте результат через конвейер (|) в less, то увидите, что строки, которые обычно пропадают (как записанные в стандартный поток ошибок), в этом случае сохраняются (так как они находятся на стандартном выводе). 3.6 Пример: stderr и stdout в файл

Это действие помещает весь вывод программы в файл. Это является подходящим вариантом для заданий cron: если Вы хотите, чтобы команда выполнялась абсолютно незаметно.
rm -f $(find / -name core) &> /dev/null

Это (предположим, для cron) удаляет любой файл с названием "core" в любом каталоге. Помните, что Вам следует полностью быть уверенным в том, что выполняет команда, если возникает желание затереть её вывод. 4. Конвейеры

Данный раздел объясняет достаточно простым и практичным способом, каким образом следует использовать конвейеры и для чего Вам это может потребоваться.
Что это такое и зачем Вам это использовать?

Конвейеры предоставляют Вам возможность использовать (автор убеждён, что это достаточно просто) вывод одной программы в качестве входа другой.

Пример: простой конвейер с sed

Это очень простой способ использования конвейеров.

ls -l | sed -e "s//u/g"

Здесь происходит следующее: первоначально выполняется команда ls;-l, и её вывод, вместо отображения на экране, отправляется в программу sed, которая, в свою очередь, выводит на экран то, что должна. 4.3 Пример: альтернатива для ls;-l;*.txt

Возможно, это значительно более сложный способ, чем ls;-l;*.txt, но он приводится здесь только для того, чтобы проиллюстрировать работу с конвейерами, а не для решения вопроса выбора из этих двух способов листинга.

ls -l | grep "\.txt$"

Здесь вывод программы ls -l отправляется в программу grep, которая, в свою очередь, выводит на экран строки, соответствующие регулярному выражению "\.txt$". 5. Переменные

Вы можете использовать переменные таким же образом, что и в любом языке программирования. Типы данных отсутствуют. Переменная в bash может представлять собой число, символ или строку символов.

Вам не следует объявлять переменную. В действительности, присвоение значения на её указатель уже создаёт её.

Пример: "Hello World!", использующий переменные

#!/bin/bash
STR="Hello World!"
echo $STR

Вторая строка создаёт переменную, которая называется STR, и присваивает ей строчное значение "Hello World!". Затем ЗНАЧЕНИЕ этой переменной извлекается добавлением в начале знака "$". Пожалуйста, запомните (постарайтесь), что если Вы не используете знак "$", вывод программы может быть другим. Вероятно, не таким, который Вам требуется.
Пример: очень простой скрипт резервного копирования (более эффективный)
#!/bin/bash
OF=/var/my-backup-$(date +%Y%m%d).tgz #OF - Output File - выходной файл
tar -cZf $OF /home/me/

Данный скрипт вводит ещё одно понятие. Прежде всего, Вам следует разобраться со второй строкой. Обратите внимание на выражение "$(date +%Y%m%d)". Если Вы запустите этот скрипт, то заметите, что он выполняет команду внутри скобок, перехватывая её вывод.

Обратите внимание, что в этом скрипте имя выходного файла будет ежедневно изменяться, исходя из формата ключа к команде date;(+%Y%m%d). Вы можете поменять это заданием другого формата.
Другие примеры:
echo ls
echo $(ls)

Локальные переменные

Локальные переменные могут быть созданы при использовании ключевого слова local.
#!/bin/bash
HELLO=Hello
function hello {
local HELLO=World
echo $HELLO
}
echo $HELLO
hello
echo $HELLO
Данного примера должно быть достаточно для отображения способов использования локальных переменных.

Условные операторы

Условные операторы предоставляют Вам возможность решить, выполнять действие или нет; решение принимается при вычислении значения выражения.

Просто теория

Существует большое количество форм условных операторов. Элементарная форма - это if выражение then оператор, где "оператор" выполняется только в том случае, если "выражение" имеет значение "истина". "2<1" - это выражение, имеющее значение "ложь", в то время как "2>1" - "истина".

Существуют другие формы условных операторов, такие как: if выражение then оператор1 else оператор2. Здесь "оператор1" выполняется, если "выражение"- истина; в противном случае, выполняется "оператор2".

Ещё одной формой условных операторов является: if выражение1 then оператор1 else if выражение2 then оператор2 else оператор3. В данной форме добавляется только последовательность "ELSE IF "выражение2" THEN "оператор2"", заставляющая "оператор2" выполняться, если "выражение2" имеет значение "истина". Всё остальное соответствует Вашему представлению об этом (см. предыдущие формы).

Несколько слов о синтаксисе:
Элементарная конструкция оператора "if" в bash выглядит следующим образом:
if [выражение];
then
code if "выражение" is true.
fi

Пример: элементарный образец условного оператора if;..;then
#!/bin/bash
if [ "foo" = "foo" ]; then

fi

Если выражением внутри квадратных скобок является истина, то выполняемый код находится после слова "then" и перед словом "fi", которое обозначает конец исполняемого при выполнении условия кода.
Пример: элементарный пример условного оператора if;..;then;...;else
#!/bin/bash
if [ "foo" = "foo" ]; then
echo-выражение вычислилось как истина
else

fi

Пример: условные операторы с переменными
#!/bin/bash
T1="foo"
T2="bar"
if [ "$T1" = "$T2" ]; then
echo-выражение вычислилось как истина
else
echo-выражение вычислилось как ложь
fi
Циклы for, while и until
В этом разделе Вы познакомитесь с циклами for, while и until.
Цикл for немного отличается от аналогов в других языках программирования. Прежде всего, он предоставляет Вам возможность выполнять последовательные действия над "словами" в строке.
Цикл while выполняет кусок кода, если тестируемым выражением является истина; и останавливается при условии, если им является ложь (или внутри исполняемого кода встречается явно заданное прерывание цикла).
Цикл until практически идентичен циклу while. Отличие заключается только в том, что код выполняется при условии, если проверяемым выражением является ложь.
Если Вы предполагаете, что while и until очень похожи, Вы правы.

Пример цикла for

#!/bin/bash
for i in $(ls); do
echo item: $i
done

Во второй строке мы представляем i в качестве переменной, которая получает различные значения, содержащиеся в $(;ls;).

При необходимости третья строка могла бы быть длиннее; или там могло бы находиться несколько строк перед done (4-я строка).

"done" (4-я строка) показывает, что код, в котором используется значение $i, заканчивается и $i получает новое значение.

Данный скрипт не предполагает большой важности. Более полезным применением цикла for было бы использование его для отбора только каких-то определённых файлов в предыдущем примере.
C-подобный for

fiesh предложил добавить эту форму цикла. Это цикл for, наиболее похожий на for в языках C, Perl и;т.п.

#!/bin/bash
for i in `seq 1 10`;
do
echo $i
done
Пример цикла while:
#!/bin/bash
COUNTER=0
while [ $COUNTER -lt 10 ]; do
echo The counter is $COUNTER
let COUNTER=COUNTER+1
done

Данный скрипт "эмулирует" широко известную (в языках C, Pascal, perl и;т.д.) структуру "for".

Пример цикла until:

#!/bin/bash
COUNTER=20
until [ $COUNTER -lt 10 ]; do
echo COUNTER $COUNTER
let COUNTER-=1
done

Функции

Аналогично любому другому языку программирования, Вы можете использовать функции для группировки кусков кода более логичным способом, а также для практического применения волшебного искусства рекурсии.

Объявление функции - это только лишь запись function my_func { my_code }.

Вызов функции осуществляется аналогичным образом, что и вызов других программ. Вы просто пишете её имя.

Пример функций
#!/bin/bash
function quit {
exit
}
function hello {
echo Hello!
}
hello
quit
echo foo
В строках 2-4 содержится функция "quit". В строках 5-7 - функция "hello". Если Вам недостаточно понятен процесс, выполняемый данным скриптом, испытайте его!

Следует заметить, что совсем необязательно объявлять функции в каком-то определённом порядке.

Если Вы запустите скрипт, обратите внимание, что сначала вызывается функция "hello", а затем функция "quit". Что касается программы, она никогда не достигает 10-й строки.

Пример функций с параметрами
#!/bin/bash
function quit {
exit
}
function e {
echo $1
}
e Hello
e World
quit
echo foo
Данный скрипт практически идентичен предыдущему. Главное отличие - это функция "e". Она выводит самый первый получаемый аргумент. Аргументы в функциях обрабатываются таким же образом, что и аргументы, переданные скрипту.

Интерфейсы пользователя

Использование select для создания простых меню
#!/bin/bash
OPTIONS="Hello Quit"
select opt in $OPTIONS; do
if [ "$opt" = "Quit" ]; then
echo done
exit
elif [ "$opt" = "Hello" ]; then
echo Hello World
else
clear
echo bad option
fi
done
Если Вы запустите этот скрипт, то увидите, что он является мечтой программиста о меню на текстовой основе. Вероятно, Вы заметите, что это очень похоже на конструкцию "for", только вместо циклической обработки каждого "слова" в $OPTIONS программа опрашивает пользователя.
Использование командной строки
#!/bin/bash
if [ -z "$1" ]; then
echo используйте: $0 каталог
exit
fi
SRCD=$1 #SRCD - SouRCe Directory - исходный каталог

tar -cZf $TGTD$OF $SRCD
Вам должно быть понятно, что выполняет этот скрипт. Выражение в первом условном операторе проверяет, получила ли программа аргумент ($1). Если - нет, оно завершает работу программы, предоставляя пользователю небольшое сообщение об ошибке. Оставшаяся на данный момент часть скрипта, очевидно, является понятной.

Разное

Чтение пользовательского ввода с помощью read

В некоторых случаях, возможно, возникнет необходимость попросить пользователя что-нибудь ввести. Существуют различные способы выполнения этого. Одним из способов является следующий:

#!/bin/bash
echo Введите, пожалуйста, Ваше имя
read NAME
echo "Привет, $NAME!"

В качестве варианта Вы можете получать сразу несколько значений с помощью read. Следующий пример поясняет это:
#!/bin/bash
echo "Введите, пожалуйста, Ваше имя и фамилию"
read FN LN #FN - First Name - имя; LN - Last Name - фамилия
echo "Hi! $LN, $FN !"

Арифметические вычисления

В командной строке (или оболочке) попробуйте ввести следующее:
echo;1;+;1
Если Вы рассчитываете увидеть "2", то будете разочарованы. Что следует выполнить, если возникает необходимость, чтобы BASH произвёл вычисления над Вашими числами? Решение заключается в следующем:
echo;$((1+1))
В результате этого вывод будет более "логичным". Такая запись используется для вычисления арифметических выражений. Вы также можете выполнить это следующим образом:
echo;$
Если Вам необходимо использовать дроби или более сложную математику, то можно использовать bc для вычисления арифметических выражений.
Когда автор запустил "echo;$" в командной оболочке, она вернула значение 0. Это связано с тем, что если bash отвечает, он использует только целые значения. Если Вы запустите "echo;3/4|bc;-l", оболочка вернёт правильное значение 0.75.

Поиск bash

Из сообщения от mike (смотрите раздел "Благодарность"):

Вы всегда используете #!/bin/bash .. Вы могли бы привести пример, каким образом можно обнаружить, где расположен bash.
Предпочтительнее использовать "locate bash", но locate имеется не на всех машинах.
"find ./ -name bash" из корневого каталога обычно срабатывает.
Можно проверить следующие расположения:
ls -l /bin/bash
ls -l /sbin/bash
ls -l /usr/local/bin/bash
ls -l /usr/bin/bash
ls -l /usr/sbin/bash
ls -l /usr/local/sbin/bash
(автор не способен сразу придумать какой-либо другой каталог... Он находил bash в большинстве этих мест на различных системах).

Вы также можете попробовать "which bash".

Получение возвратного значения программы

В bash возвратное значение программы сохраняется в специальной переменной $?.

Данный пример иллюстрирует, как перехватить возвратное значение программы; автор предположил, что каталог dada не существует (это также предложил mike).
#!/bin/bash
cd /dada &> /dev/null
echo rv: $?
cd $(pwd) &> /dev/null
echo rv: $?


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

Этот небольшой скрипт представляет все таблицы из всех баз данных (предполагается, что у Вас установлен MySQL). Кроме того, следует подумать о способах преобразования команды "mysql" для использования подходящего имени пользователя и пароля.
#!/bin/bash
DBS=`mysql -uroot -e"show databases"`
for b in $DBS ;
do
mysql -uroot -e"show tables from $b"
done

Несколько исходных файлов

Вы можете запускать несколько файлов с помощью команды source.

TO-DO__
11. Таблицы
11.1 Операторы сравнения строк
(1) s1 = s2
(2) s1 != s2
(3) s1 < s2
(4) s1 > s2
(5) -n s1
(6) -z s1
(1) s1 совпадает с s2
(2) s1 не совпадает с s2
(3) s1 в алфавитном порядке предшествует s2 (в соответствии с текущей локалью)
(4) s1 в алфавитном порядке следует после s2 (в соответствии с текущей локалью)
(5) s1 имеет ненулевое значение (содержит один символ или более)
(6) s1 имеет нулевое значение
11.2 Примеры сравнения строк

Сравнение двух строк.
#!/bin/bash
S1="string"
S2="String"
if [ $S1=$S2 ];
then
echo "S1("$S1") не равна to S2("$S2")"
fi
if [ $S1=$S1 ];
then
echo "S1("$S1") равна to S1("$S1")"
fi
На данный момент, автор считает необходимым процитировать замечание из письма, полученного от Андреаса Бека, которое связано с использованием if [ $1 = $2 ].
Это не является хорошей идеей, так как если либо $S1, либо $S2 - пустая строка, Вы получите синтаксическую ошибку. Более приемлимым будет использование x$1;=;x$2 или "$1";=;"$2" .
11.3 Arithmetic operators
+
-
*
% (remainder)
11.4 Арифметические операторы сравнения
-lt (<)
-gt (>)
-le (<=)
-ge (>=)
-eq (==)
-ne (!=)
Программистам на C необходимо просто выбрать оператор, соответствующий выбранному оператору в скобках.

Полезные команды

Этот раздел переписал Kees (смотрите раздел "Благодарность").

Некоторые из этих команд практически содержат полноценные командные языки. Здесь объясняются только основы таких команд. Для более подробной информации внимательно просмотрите man-страницы каждой команды.
sed (потоковый редактор)
Sed - это неинтерактивный редактор. Вместо того, чтобы изменять файл движением курсора на экране, следует использовать сценарий инструкций по редактированию для sed, а также имя редактируемого файла. Вы также можете рассматривать sed в качестве фильтра. Посмотрите на некоторые примеры:
$sed "s/to_be_replaced/replaced/g" /tmp/dummy
Sed заменяет строку "to_be_replaced" строкой "replaced", читая файл /tmp/dummy . Результат отправляется на стандартный вывод (обычно, на консоль), но Вы также можете добавить ">;capture" в вышеуказанную строку, чтобы sed отправлял вывод в файл "capture".
$sed 12, 18d /tmp/dummy
Sed отображает все строки, за исключением строк с 12 по 18. Исходный файл этой командой не изменяется.
awk (манипулирование файлами данных, выборка и обработка текста)
Существует большое количество реализаций языка программирования AWK (наиболее распространенными интерпретаторами являются gawk из проекта GNU и "новый awk" mawk.) Принцип достаточно прост: AWK находится в поиске шаблона; для каждого подходящего шаблона выполняется какое-нибудь действие.
Автор повторно создал файл dummy, содержащий следующие строки:
"test123
test
tteesstt"

$awk "/test/ {print}" /tmp/dummy
test123
test
Шаблон, искомый AWK, это "test", а действие, выполняемое AWK при обнаружении строки в /tmp/dummy с подстрокой "test", это "print".
$awk "/test/ {i=i+1} END {print i}" /tmp/dummy
Если Вы находитесь в поиске нескольких шаблонов, замените текст между кавычками на "-f;file.awk". В этом случае, Вы можете записать все шаблоны и действия в файле "file.awk".
grep (выводит строки, соответствующие искомому шаблону)
Мы рассматривали несколько команд grep в предыдущих главах, которые отображали строки, соответствующие шаблону. Однако grep способен выполнять значительно большее.
$grep "look for this" /var/log/messages -c
Строка "look for this" была обнаружена 12 раз в файле /var/log/messages.
wc (считает строки, слова и байты)
В следующем примере можно заметить, что выводится не то, что мы ожидаем. В этом случае, файл dummy содержит следующий текст:
"bash introduction
howto test file"
$wc --words --lines --bytes /tmp/dummy
2 5 34 /tmp/dummy
wc не заботится о порядке параметров. Он всегда выводит их в стандартном порядке: <число;строк><число;слов><число;байтов><имя;файла>.
sort (сортирует строки текстового файла)

В этом случае, файл dummy содержит следующий текст:
"b
c
a"
$sort /tmp/dummy
Вывод выглядит следующим образом:
a
b
c
Команды не должны быть такими простыми:-)
bc (вычислительный язык программирования)
bc производит вычисления с командной строки (ввод из файла, но не через перенаправление или конвейер), а также из пользовательского интерфейса. Следующий пример показывает некоторые команды. Обратите внимание, что автор использовал bc с параметром -q, чтобы отказаться от вывода сообщения с приглашением.
$bc -q
1 == 5
0
0.05 == 0.05
1
5 != 5
0
2 ^ 8
256
sqrt(9)
3
while (i != 9) {
i = i + 1;
print i
}
123456789
quit
tput (инициализирует терминал или запрашивает базу данных terminfo)
Небольшая иллюстрация возможностей tput:

$tput cup 10 4
Приглашение командной строки появится в координатах (y10,x4).
$tput reset
Экран очищается и приглашение появляется в (y1,x1). Обратите внимание, что (y0,x0) - это левый верхний угол.
$tput cols
80 Отображает возможное количество символов в направлении по оси x.
Настоятельно рекомендуется быть с этими программами на "ты" (как минимум). Существует огромное количество небольших программ, которые предоставляют Вам возможность заняться настоящей магией в командной строке.
[Некоторые примеры были заимствованы из man-страниц или FAQ.]

Ещё скрипты

Применение команды ко всем файлам в каталоге.

Пример: очень простой скрипт резервного копирования (более эффективный)

#!/bin/bash
SRCD="/home/" #SRCD - SouRCe Directory - исходный каталог
TGTD="/var/backups/" #TGTD - TarGeT Directory - конечный каталог
OF=home-$(date +%Y%m%d).tgz #OF - Output File - выходной файл
tar -cZf $TGTD$OF $SRCD

Программа переименования файлов

#!/bin/sh
# renna: переименование нескольких файлов по специальным правилам
# Автор - felix hudson Jan - 2000

#Прежде всего, посмотрите на различные "режимы", которые имеются у этой программы.
#Если первый аргумент ($1) является подходящим, мы выполняем эту часть
#программы и выходим.

# Проверка на возможность добавления префикса.
if [ $1 = p ]; then

#Теперь переходим от переменной режима ($1) и префикса ($2)
prefix=$2 ; shift ; shift

# Необходимо проверить, задан ли, по крайней мере, хотя бы один файл.
# В противном случае, лучше ничего не предпринимать, чем переименовывать несуществующие
# файлы!!

if [$1 = ]; then

exit 0
fi

# Этот цикл for обрабатывает все файлы, которые мы задали
# программе.
# Он осуществляет одно переименование на файл.
for file in $*
do
mv ${file} $prefix$file
done

#После этого выполняется выход из программы.
exit 0
fi

# Проверка на условие добавления суффикса.
# В остальном, данная часть фактически идентична предыдущему разделу;
# пожалуйста, смотрите комментарии, содержащиеся в нем.
if [ $1 = s ]; then
suffix=$2 ; shift ; shift
if [$1 = ]; then
echo "не задано ни одного файла"
exit 0
fi
for file in $*
do
mv ${file} $file$suffix
done
exit 0
fi

# Проверка на условие переименования с заменой.
if [ $1 = r ]; then
shift
# Из соображений безопасности автор включил эту часть, чтобы не повредить ни один файл, если пользователь
# не определил, что следует выполнить:
if [ $# -lt 3 ] ; then
echo "Ошибка; правильный ввод: renna r [выражение] [замена] файлы... "
exit 0
fi

# Рассмотрим другую информацию
OLD=$1 ; NEW=$2 ; shift ; shift

# Данный цикл for последовательно проходит через все файлы, которые мы
# задали программе.
# Он совершает одно переименование на файл, используя программу "sed".
# Это простая программа с командной строки, которая анализирует стандартный
# ввод и заменяет регулярное выражение на заданную строку.
# Здесь мы задаём для sed имя файла (в качестве стандартного ввода) и заменяем
# необходимый текст.
for file in $*
do
new=`echo ${file} | sed s/${OLD}/${NEW}/g`
mv ${file} $new
done
exit 0
fi
# Если мы достигли этой строки, это означает, что программе были заданы
# неправильные параметры. В связи с этим, следует объяснить пользователю, как её
# использовать
echo "используйте:"
echo " renna p [префикс] файлы.."
echo " renna s [суффикс] файлы.."
echo " renna r [выражение] [замена] файлы.."
exit 0
# done!

Программа переименования файлов (простая)
#!/bin/bash
# renames.sh
# простая программа переименования

criteria=$1
re_match=$2
replace=$3

For i in $(ls *$criteria*);
do
src=$i
tgt=$(echo $i | sed -e "s/$re_match/$replace/")
mv $src $tgt
done

Если происходящее отличается от ожидаемого (отладка)

Каким образом можно вызвать BASH?

Было бы неплохо добавить в первую строку

#!/bin/bash -x
В результате этого будет выводиться некоторая интересная выходная информация.

О документе

Не следует стесняться вносить исправления, дополнения или что-либо ещё, если, по Вашему мнению, это должно присутствовать в этом документе. Автор постарается по возможности обновить его.

В данной шпаргалке затрагиваются следующие темы: введение в оболочку, навигация, основные команды, переменные окружения, коннекторы, конвейеры, перенаправление ввода/вывода, права доступа и комбинации клавиш.

Оболочка Bash: введение

Оболочка, или шелл (shell) - это программа, в нашем случае названная «bash», что является сокращением от Bourne Again Shell. Оболочка принимает ваши команды и передаёт их операционной системе. Для взаимодействия с системой используются терминалы, такие как gnome-terminal , eterm , nxterm и т. п.

Навигация

В Linux файлы и каталоги имеют иерархическую организацию, то есть существует некий начальный каталог, называемый корневым. В нём содержатся файлы и подкаталоги, которые в свою очереди содержат файлы и свои подкаталоги.

pwd

Команда pwd , сокращение от print working directory , отображает текущее местоположение в структуре каталогов.

cd

Команда cd позволяет перейти в новый каталог.

mkdir

Команда mkdir создаёт новый каталог в текущем каталоге.

Основные команды

man

Команда man отображает руководства по командам. Например, следующая команда выдаст всю информацию о команде cat:

$ man cat

cat

Команда cat считывает файл, переданный как аргумент, и выводит его содержимое по стандартному каналу вывода. Передача нескольких файлов в виде аргумента приведёт к выводу конкатенированного содержимого всех файлов.

echo

Команда echo выводит свои аргументы по стандартному каналу вывода.

$ echo Hello World Hello World

Если вызвать echo без аргументов, будет выведена пустая строка.

head

Команда head читает первые 10 строк любого переданного текста и выводит их по стандартному каналу. Число выводимых строк можно изменить:

$ head -50 test.txt

tail

Команда tail работает аналогично команде head , но читает строки с конца:

$ tail -50 test.txt

Также можно просматривать добавляемые к файлу строки в режиме реального времени при помощи флага -f:

$ tail -f test.txt

less

Команда less позволяет перемещаться по переданному файлу или куску текста, причём в обоих направлениях.

$ less test.txt $ ps aux | less

Подробнее о назначении символа | будет рассказано ниже в разделе команды history .

Обычные сочетания клавиш Описание
G Перемещает в конец файла
g Перемещает в начало файла
:50 Перемещает на 50 строку файла
q Выход из less
/searchterm Поиск строки, совпадающей с ‘searchterm’, ниже текущей строки
/
?searchterm Поиск строки, совпадающей с ‘searchterm’, выше текущей строки
? Перемещает на следующий подходящий результат поиска
up Перемещает на одну строку выше
down Перемещает на одну строку ниже
pageup Перемещает на одну страницу выше
pagedown Перемещает на одну страницу ниже

true

Команда true всегда возвращает ноль в качестве выходного статуса для индикации успеха.

false

Команда false всегда возвращает не-ноль в качестве выходного статуса для индикации неудачи.

$?

$? - это переменная, которая содержит выходной статус последней запущенной команды. Под статусом обычно понимается код возврата программы. 0 означает успешное выполнение программы, любое значение большее 0 отражает тот факт, что в процессе выполнения возникли некоторые ошибки. Кстати, именно поэтому в bash истинной (true) считается 0, а все, что не 0 - ложью (false):

$ true $ echo $? 0 $ false $ echo $? 1

grep

Команда grep занимается поиском переданной строки в указанном файле:

$ cat users.txt user:student password:123 user:teacher password:321 $ grep "student` file1.txt user:student password:123

grep также может принимать несколько файлов и регулярных выражений для уточнения формата текста.

history

Команда history выводит историю командной строки. Обычно её используют вместе с командой grep для поиска конкретной команды. Например, следующий код найдёт все команды, содержащие строку g++ :

$ history | grep g++ 155 g++ file1.txt 159 g++ file2.txt

Здесь также используется символ | - это так называемый конвейер (pipe). Благодаря ему можно перенаправлять вывод одной команды на вход другой - таким образом в примере выше вся история, которая в обычном режиме выводится командой history прямо в вывод терминала, будет перенаправлена в grep в качестве входных данных. Мы не увидим вывода команды history , но увидим вывод команды grep .

Это может быть довольно сложно для понимания без практики, поэтому поэкспериментируйте самостоятельно, например с командами ls , history , ps (описана ниже), перенаправляя их вывод в grep , sed или less , например.

export

Команда export устанавливает переменные окружения для передачи дочерним процессам. Например, так можно передать переменную name со значением student:

$ export name=student

ps

Команда ps выводит информацию о запущенных процессах.

$ ps PID TTY TIME CMD 35346 pts/2 00:00:00 bash

Выводится четыре элемента:

  • ID процесса (PID),
  • тип терминала (TTY),
  • время работы процесса (TIME),
  • имя команды, запустившей процесс (CMD).

awk

Команда awk находит и заменяет текст в файлах по заданному шаблону: awk "pattern {action}" test.txt

wget

Команда wget скачивает файлы из Сети и помещает их в текущий каталог.

$ wget https://github.com/mikeizbicki/ucr-cs100

nc

ping

Команда ping тестирует сетевое подключение.

$ ping google.com PING google.com (74.125.224.34) 56(84) bytes of data. 64 bytes from lax17s01-in-f2.1e100.net (74.125.224.34): icmp_req=1 ttl=57 time=7.82 ms --- google.com ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 8ms rtt min/avg/max/mdev = 7.794/8.422/10.792/0.699 ms

Статистика в конце показывает количество подключений, совершённых до завершения команды, и время их выполнения.

git

Переменные окружения

Переменные окружения - это именованные переменные, содержащие значения, используемые одним или несколькими приложениями.

Переменная PATH содержит список каталогов, в которых система ищет исполняемые файлы.

Переменная HOME содержит путь к домашнему каталогу текущего пользователя.

Коннекторы

Коннекторы позволяют запускать несколько команд одновременно.

$ true && echo Hello Hello $ false || echo Hello Hello $ echo Hello ; ls Hello test.txt file1.txt file2.txt

Конвейеры

Конвейеры, или пайпы, позволяют соединять входные и выходные каналы различных команд. В следующем примере вывод команды ls будет передан в head , и в результате будет напечатано лишь 10 первых элементов.

$ ls -l | head

Перенаправление ввода/вывода

Перенаправление вывода

Для стандартного перенаправления вывода используются символы > и >> .

Например, этот код передаст вывод ls в файл, а не на экран:

$ ls > files.txt $ cat files.txt file1.cpp sample.txt

Если файл не существует, он создаётся, а если существует, то перезаписывается. Во избежание перезаписи стоит использовать команду >> - она дописывает данные в конец файла.

Перенаправление ввода

Для стандартного перенаправления вывода используется символ < . В следующем примере sort берет входные данные из файла, а не с клавиатуры:

$ cat files.txt c b $ sort < files.txt b c

Команда sort выводит содержимое файла на экран, поскольку мы не перенаправили выход. Это можно сделать так:

$ sort < files.txt > files_sorted.txt

Продвинутое перенаправление

Добавление & к > приводит к перенаправлению как стандартного потока выхода, так и потока ошибок. Например, файл test.cpp выведет строку stdout в cout и строку stderr в cerr .

$ g++ test.cpp $ ./a.out >& test.txt $ cat test.txt stdout stderr

Если вы хотите вывести конкретный файловый дескриптор, вы можете приписать его номер к > .

Имя Дескриптор Описание
stdin 0 Стандартный поток ввода
stdout 1 Стандартный поток вывода
stderr 2 Стандартный поток вывода ошибок

Например, для перенаправления stderr в test.txt нужно сделать следующее:

$ g++ test.cpp $ ./a.out 2> test.txt stdout $ cat test.txt stderr

Права доступа

Команда ls -l выводит много информации о права доступа к каждому файлу:

chmod

Команда chmod изменяет права доступа файла. Вот типичные сочетания флагов для изменения прав конкретных пользователей:

Вы можете вызвать chmod с описанием действий над конкретным файлом. Символ - обозначает удаление прав, символ + - добавление. Следующий пример сделает файл доступным для чтения и записи владельцу и группе:

$ chmod ug+rw test.txt $ ls -l test.txt -rw-rw---- 1 user group 1097374 January 26 2:48 test.txt

Кроме того, chmod можно использовать с восьмеричными числами, где 1 - это наличие прав, а 0 - отсутствие:

Rwx = 111 = 7 rw- = 110 = 6 r-x = 101 = 5 r-- = 100 = 4

Следующая команда сработает так же, как и предыдущая.