Bash is a directory что это
Перейти к содержимому

Bash is a directory что это

  • автор:

Командная​ ​строка​ ​Ubuntu: ​команды​ ​bash

В этой статье мы рассмотрим, что такое командная строка Ubuntu cmd (Ubuntu относится к семейству ОС Linux), как её открыть, а также опишем основные команды bash.

Командная строка Ubuntu

Работать в Ubuntu и других Linux-подобных операционных системах можно с помощью двух разных интерфейсов — графического интерфейса и командной строки.

Графический интерфейс пользователя (GUI — Graphical User Interface) — это привычное многим управление с помощью рабочего стола, графических окон, меню и кнопок. Пользователь может щелчками мыши открывать программы, копировать и удалять файлы, создавать папки и т. д.

Другой интерфейс — это командная строка (CLI — Command Line Interface). Пользователь вводит в неё определённые команды (набор букв, цифр и символов) с помощью клавиатуры, и компьютер запускает соответствующие процессы.

Открыть командную строку в Линукс-системах можно через консоль или терминал. В Ubuntu консоль запускается при загрузке системы. Терминал — это тоже консоль, но уже в графической оболочке. Его можно запустить, набрав слово Терминал в поисковой строке ОС, или через комбинацию клавиш Ctrl+Alt+T.

Консоль и терминал обрабатывают команды с помощью программной оболочки: в Ubuntu это командный интерпретатор bash. Он распознаёт команды, которые пользователь ввёл в командной строке, и выполняет их.

Ниже рассмотрим, из чего состоит команда, а также приведём используемый в bash список команд.

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

В общем виде в Ubuntu команды имеют такой вид:

программа -ключ значение

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

Ключ — обычно у каждой программы свой набор ключей. Их можно найти в мануале к программе.

Значение — параметры программы: цифры, буквы, символы, переменные.

Напомним, что для выполнения команды нужно ввести её в командную строку — Ubuntu console или эмулирующий работу консоли терминал.

Рассмотрим основные команды консоли Ubuntu:

Промежуточная команда sudo (SuperUser DO — суперпользователь) позволяет запускать программы от имени администратора или root-пользователя.

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

Команда apt-get используется для работы с программными пакетами для установки программных пакетов ( sudo apt-get install имя-пакета ), обновления репозитория с пакетами ( sudo apt-get update ) и обновления пакетов, которые установлены в систему ( sudo apt-get upgrade ).

Команда pwd (print working directory — вывести рабочую директорию) показывает полное имя рабочей директории, в которой вы находитесь.

Команда ls (list — список) выводит все файлы во всех папках рабочей директории.

С помощью ls -a можно вывести и скрытые файлы.

Команда cd (change directory — изменить директорию) позволяет перейти в другую директорию.

Можно ввести как полный путь до папки, так и её название. Например, чтобы попасть в папку Files, лежащую в директории /user/home/Files, введите cd Files или cd /user/home/Files .

Чтобы попасть в корневую директорию, введите cd / .

Команда cp (copy — копировать) копирует файл.

Например, cp file1 file2 скопирует содержимого файла file1 в file2 .

Команда cp file /home/files скопирует файл с названием file в директорию /home/files.

Команда mv (move — переместить) помогает перемещать файлы.

Также с помощью mv можно переименовывать файлы. Например, у нас есть файл file.txt. С помощью команды mv file.txt new_file.txt мы можем перенести его в ту же директорию, но у файла уже будет новое название new_file.txt.

Команда rm (remove — удалить) удаляет файлы и каталоги.

Так, команда rm file.txt удалит текстовый файл с названием file, а команда rm -r Files удалит директорию Files со всеми содержащимися в ней файлами.

С помощью mkdir (make directory — создать директорию) можно создать новую директорию.

Так, команда mkdir directory создаст новую директорию с именем directory в текущей рабочей директории.

Команда man (manual — мануал) открывает справочные страницы с подробной информацией о команде.

Введите man , а затем через пробел название команды, о которой вы хотите узнать подробнее. Например, man cp выведет справочную страницу о команде cp.

Мы рассмотрели, как вызвать командную строку в Linux — открыть терминал или запустить консоль в Ubuntu, а также перечислили основные команды терминала Ubuntu.

Помогла ли вам статья?

Спасибо за оценку. Рады помочь ��

Bash-скрипты: начало

Сегодня поговорим о bash-скриптах. Это — сценарии командной строки, написанные для оболочки bash. Существуют и другие оболочки, например — zsh, tcsh, ksh, но мы сосредоточимся на bash. Этот материал предназначен для всех желающих, единственное условие — умение работать в командной строке Linux.

Сценарии командной строки — это наборы тех же самых команд, которые можно вводить с клавиатуры, собранные в файлы и объединённые некоей общей целью. При этом результаты работы команд могут представлять либо самостоятельную ценность, либо служить входными данными для других команд. Сценарии — это мощный способ автоматизации часто выполняемых действий.

Итак, если говорить о командной строке, она позволяет выполнить несколько команд за один раз, введя их через точку с запятой:

pwd ; whoami

На самом деле, если вы опробовали это в своём терминале, ваш первый bash-скрипт, в котором задействованы две команды, уже написан. Работает он так. Сначала команда pwd выводит на экран сведения о текущей рабочей директории, потом команда whoami показывает данные о пользователе, под которым вы вошли в систему.

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

getconf ARG_MAX

Командная строка — отличный инструмент, но команды в неё приходится вводить каждый раз, когда в них возникает необходимость. Что если записать набор команд в файл и просто вызывать этот файл для их выполнения? Собственно говоря, тот файл, о котором мы говорим, и называется сценарием командной строки.

Как устроены bash-скрипты

Создайте пустой файл с использованием команды 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 Проверяет строки на равенство, возвращает истину, если строки идентичны.
s tr1 != 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 Script Step By Step — здесь речь идёт о том, как начать создание bash-скриптов, рассмотрено использование переменных, описаны условные конструкции, вычисления, сравнения чисел, строк, выяснение сведений о файлах.

Уважаемые читатели! Просим гуру bash-программирования рассказать о том, как они добрались до вершин мастерства, поделиться секретами, а от тех, кто только что написал свой первый скрипт, ждём впечатлений.

What does set -e mean in a bash script?

I’m studying the content of this preinst file that the script executes before that package is unpacked from its Debian archive (.deb) file. The script has the following code:

#!/bin/bash set -e # Automatically added by dh_installinit if [ "$1" = install ]; then if [ -d /usr/share/MyApplicationName ]; then echo "MyApplicationName is just installed" return 1 fi rm -Rf $HOME/.config/nautilus-actions/nautilus-actions.conf rm -Rf $HOME/.local/share/file-manager/actions/* fi # End automatically added section 

My first query is about the line:

set -e 

I think that the rest of the script is pretty simple: It checks whether the Debian/Ubuntu package manager is executing an install operation. If it is, it checks whether my application has just been installed on the system. If it has, the script prints the message «MyApplicationName is just installed» and ends ( return 1 mean that ends with an “error”, doesn’t it?). If the user is asking the Debian/Ubuntu package system to install my package, the script also deletes two directories. Is this right or am I missing something?

3,101 2 2 gold badges 31 31 silver badges 48 48 bronze badges
asked Oct 27, 2013 at 19:06
AndreaNobili AndreaNobili
41.2k 107 107 gold badges 327 327 silver badges 605 605 bronze badges
Oct 27, 2013 at 19:07

reason why you couldn’t find this in google: -e in your query is interpreted as negation. Try following query: bash set «-e»

Oct 31, 2014 at 22:08
@twalberg When I’ve asked myself the same question, I was looking at man set
Sep 3, 2017 at 17:09
if you’re looking how to turn it off, swap the dash to a plus prefix: set +e
Mar 13, 2018 at 6:37

@twalberg but asking real people is so much more interesting than just making a request from a robot ;-).

Apr 29, 2018 at 2:45

10 Answers 10

 -e Exit immediately if a command exits with a non-zero status. 

But it’s considered bad practice by some (bash FAQ and irc freenode #bash FAQ authors). It’s recommended to use:

trap 'do_something' ERR 

to run do_something function when errors occur.

720 1 1 gold badge 12 12 silver badges 29 29 bronze badges
answered Oct 27, 2013 at 19:39
Gilles Quénot Gilles Quénot
176k 41 41 gold badges 227 227 silver badges 226 226 bronze badges

What would the do_something be if I wanted the same semantics as «Exit immediately if a command exits with a non-zero status»?

May 14, 2014 at 2:37

The ERR trap is not inherited by shell functions, so if you have functions, set -o errtrace or set -E will allow you to just set the trap once and apply it globally.

Jul 17, 2015 at 18:21
does trap ‘exit’ ERR do anything different from set -e ?
Jul 11, 2016 at 20:58
if it’s bad practice then why it’s used in Debian packages?
Dec 7, 2016 at 6:35

It’s not universally considered bad practice. As with many disfavored language construct, it has its place. The main problem with it is that the behavior in edge cases is somewhat non-intuitive.

Dec 23, 2016 at 21:26

set -e stops the execution of a script if a command or pipeline has an error — which is the opposite of the default shell behaviour, which is to ignore errors in scripts. Type help set in a terminal to see the documentation for this built-in command.

answered Oct 27, 2013 at 19:14
Robin Green Robin Green
32.2k 16 16 gold badges 104 104 silver badges 187 187 bronze badges

It only stops execution if the last command in a pipeline has an error. There’s a Bash specific option, set -o pipefail which can be used to propagate errors so that the return value of the pipeline command is non-zero if one of the preceding commands exited with a non-zero status.

Nov 9, 2015 at 22:25

Keep in mind that -o pipefail means only that the exit status of the first non-zero (i.e. erroring in -o errexit terms) command of the pipeline is propagated to the end. The remaining commands in the pipeline still run, even with set -o errexit . For example: echo success | cat — <(echo piping); echo continues , where echo success represents a successful, but fallible command, will print success , piping , and continues , but false | cat - <(echo piping); echo continues , with false representing the command now erroring silently, will still print piping before exiting.

Aug 23, 2019 at 7:26

I found this post while trying to figure out what the exit status was for a script that was aborted due to a set -e . The answer didn’t appear obvious to me; hence this answer. Basically, set -e aborts the execution of a command (e.g. a shell script) and returns the exit status code of the command that failed (i.e. the inner script, not the outer script).

For example, suppose I have the shell script outer-test.sh :

#!/bin/sh set -e ./inner-test.sh exit 62; 

The code for inner-test.sh is:

#!/bin/sh exit 26; 

When I run outer-script.sh from the command line, my outer script terminates with the exit code of the inner script:

$ ./outer-test.sh $ echo $? 26 

3 2 2 bronze badges
answered Aug 5, 2016 at 22:54
10.2k 8 8 gold badges 47 47 silver badges 69 69 bronze badges
Thanks for the detail about the return status code :+1:.
Mar 8 at 20:57

As per bash — The Set Builtin manual, if -e / errexit is set, the shell exits immediately if a pipeline consisting of a single simple command, a list or a compound command returns a non-zero status.

By default, the exit status of a pipeline is the exit status of the last command in the pipeline, unless the pipefail option is enabled (it’s disabled by default).

If so, the pipeline’s return status of the last (rightmost) command to exit with a non-zero status, or zero if all commands exit successfully.

If you’d like to execute something on exit, try defining trap , for example:

trap onexit EXIT 

where onexit is your function to do something on exit, like below which is printing the simple stack trace:

onexit()

There is similar option -E / errtrace which would trap on ERR instead, e.g.:

trap onerr ERR 

Examples

Zero status example:

$ true; echo $? 0 

Non-zero status example:

$ false; echo $? 1 

Negating status examples:

$ ! false; echo $? 0 $ false || true; echo $? 0 

Test with pipefail being disabled:

$ bash -c 'set +o pipefail -e; true | true | true; echo success'; echo $? success 0 $ bash -c 'set +o pipefail -e; false | false | true; echo success'; echo $? success 0 $ bash -c 'set +o pipefail -e; true | true | false; echo success'; echo $? 1 

Test with pipefail being enabled:

$ bash -c 'set -o pipefail -e; true | false | true; echo success'; echo $? 1 

1 1 1 silver badge
answered Dec 20, 2015 at 13:31
157k 88 88 gold badges 680 680 silver badges 747 747 bronze badges

This is an old question, but none of the answers here discuss the use of set -e aka set -o errexit in Debian package handling scripts. The use of this option is mandatory in these scripts, per Debian policy; the intent is apparently to avoid any possibility of an unhandled error condition.

What this means in practice is that you have to understand under what conditions the commands you run could return an error, and handle each of those errors explicitly.

Common gotchas are e.g. diff (returns an error when there is a difference) and grep (returns an error when there is no match). You can avoid the errors with explicit handling:

diff this that || echo "$0: there was a difference" >&2 grep cat food || echo "$0: no cat in the food" >&2 

(Notice also how we take care to include the current script’s name in the message, and writing diagnostic messages to standard error instead of standard output.)

If no explicit handling is really necessary or useful, explicitly do nothing:

diff this that || true grep cat food || : 

(The use of the shell’s : no-op command is slightly obscure, but fairly commonly seen.)

Just to reiterate,

something || other 

is shorthand for

if something; then : nothing else other fi 

i.e. we explicitly say other should be run if and only if something fails. The longhand if (and other shell flow control statements like while , until ) is also a valid way to handle an error (indeed, if it weren’t, shell scripts with set -e could never contain flow control statements!)

And also, just to be explicit, in the absence of a handler like this, set -e would cause the entire script to immediately fail with an error if diff found a difference, or if grep didn’t find a match.

On the other hand, some commands don’t produce an error exit status when you’d want them to. Commonly problematic commands are find (exit status does not reflect whether files were actually found) and sed (exit status won’t reveal whether the script received any input or actually performed any commands successfully). A simple guard in some scenarios is to pipe to a command which does scream if there is no output:

find things | grep . sed -e 's/o/me/' stuff | grep ^ 

It should be noted that the exit status of a pipeline is the exit status of the last command in that pipeline. So the above commands actually completely mask the status of find and sed , and only tell you whether grep finally succeeded.

(Bash, of course, has set -o pipefail ; but Debian package scripts cannot use Bash features. The policy firmly dictates the use of POSIX sh for these scripts, though this was not always the case.)

In many situations, this is something to separately watch out for when coding defensively. Sometimes you have to e.g. go through a temporary file so you can see whether the command which produced that output finished successfully, even when idiom and convenience would otherwise direct you to use a shell pipeline.

Введение в Bash Shell

Всем привет. Это перевод из книги по подготовке к экзамену RedHat RHCE. На мой взгляд очень доступно рассказывается об основах bash.

Сценарии оболочки — наука сама по себе. Не вдаваясь в подробности всего, что происходит «под капотом», вы узнаете, как применять базовые элементы для написания собственных скриптов, и анализировать, что происходит в сторонних сценариях оболочки.

Понимание основных элементов сценариев оболочки

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

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

Ниже показан очень простой скрипт:

#!/bin/bash # # #This is a script that greets the world # Usage: ./hello clear echo hello world exit 0 

Здесь содержатся несколько элементов, которые должны использоваться во всех скриптах. Для начала, есть shebang — это строка #!/bin/bash. Когда скрипт запускается из родительской оболочки, он открывает подоболочку, в которой и выполняются команды, указанные в скрипте.

Эти команды могут быть интерпретированы различными способами. Для того, чтобы понять, как именно они должны интерпретироваться, используется shebang. В примере выше shebang ясно даёт понять, что скрипт должен выполняться оболочкой bash.

Также могут быть указаны другие оболочки. Например, если ваш скрипт содержит код на языке Perl, shebang должен быть #!/usr/bin/perl. Начинать сценарий с shebang является хорошей практикой; если он опущен, код сценария будет выполняться такой же оболочкой, которая используется для запуска скрипта.

Сразу после shebang расположена часть, объясняющая, о чем сценарий. Несколько строк комментариев в начале каждого сценария — хорошая идея. В коротком скрипте часто очевидно, что он делает, но когда сценарий становится длиннее, и по мере того, как всё больше людей вовлекаются в его написание и поддержку, становится менее понятно, что авторы намереваются сделать.

Чтобы избежать такой ситуации, убедитесь, что вы добавили строки комментариев, начиная каждую символом #. Комментарии могут быть не только в первых строках, но и в начале каждого подраздела сценария. Это наверняка поможет, если вы прочитаете свой скрипт через несколько месяцев!

Вы также можете комментировать не только подразделы, но и отдельные строки.

Независимо от того, в какой позиции он используется, всё от символа # и до конца строки является комментарием.

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

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

Полезно знать, что вы можете работать с exit, чтобы сообщить родительской оболочке, как все прошло.

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

После создания сценария убедитесь, что он может быть выполнен. Самый распространённый способ сделать это — применить к нему бит выполнения. Итак, если имя файла скрипта — hello, используйте команду chmod +x ./hello для того, чтобы сделать его исполняемым.

Сценарий также может быть выполнен в качестве аргумента команды bash. В этом случае введите bash ./hello для запуска скрипта hello. Если сценарий запускается как аргумент команды bash, файл скрипта не обязательно должен быть исполняемым.

По сути, вы можете хранить скрипт в любом месте, но если вы собираетесь хранить его в каталоге, который не включён в переменную $PATH, вам нужно выполнить его с ./ перед именем скрипта.

Введите ./hello для того, чтобы запустить сценарий, или поместите его в стандартный каталог, который включён в переменную $PATH, например, /usr/local/bin.

Также вы можете разместить сценарий в каталоге /bin, после чего в любом месте файловой системы просто ввести имя файла, и сценарий выполнится.

Командой vi /bin/datetime создадим в каталоге /bin файл с именем datetime. В созданный файл вставим это содержимое:

#!/bin/bash # выводит дату и время. а так же список пользователей, которые залогинены в данный момент date who 

Сохранив файл, введите chmod +x /bin/datetime, чтобы дать файлу права на выполнение. Перейдите, к примеру, в домашний каталог с помощью команды cd ~ и просто введите datetime.

Перейдите, к примеру, в домашний каталог cd ~ и просто введите datetime.

[root@localhost ~]# datetime Sat Sep 28 00:33:41 EDT 2019 root tty1 2019-09-25 20:28 root pts/0 2019-09-27 20:07 (comp.corp.domain.ru) 

Использование переменных и входных данных

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

Использование позиционных параметров

При запуске скрипта можно использовать аргументы. Аргумент — это всё, что вы помещаете за командой сценария. Аргументы могут быть использованы для того, чтобы сделать скрипт более гибким. Возьмём команду useradd lisa. В этом примере команда — это useradd, а её аргумент — lisa — указывает, что нужно сделать.

В результате выполнения такой команды должен быть создан пользователь с именем lisa.

В тексте сценария первый аргумент обозначается $1, второй аргумент — $2 и т. д. Листинг 1 показывает, как можно использовать аргументы. Попробуйте запустить этот код, указав в качестве параметров любые имена пользователей.

#!/bin/bash # run this script with a few arguments echo The first argument is $1 echo The second argument is $2 echo The third argument is $3 

Под параметрами подразумевается ввод данных перед запуском скрипта. В данном случае в качестве параметров после имени скрипта argument я указал lisa, lori и bob:

[root@server1 ~]# ./argument lisa lori bob The first argument is lisa The second argument is lori The third argument is bob [root@server1 ~]# 

Если вы попытались запустить пример кода, то могли заметить, что его содержимое не идеально. Если вы используете три аргумента при выполнении скрипта из листинга 1, он будет работать отлично. Если используете только два аргумента, третий выводится без значения $3.

Если же используете четыре аргумента, четвёртое значение (которое будет храниться в $4) никогда не отобразится. Итак, если вы хотите использовать аргументы, вам лучше использовать более гибкий подход.

#!/bin/bash # run this script with a few arguments echo you have entered $# arguments for i in $@ do echo $i done exit 0 

В Листинге 2 представлены два новых элемента, которые относятся к аргументам:

  • $# — это счетчик, который показывает, сколько аргументов было использовано при запуске скрипта.
  • $@ — список всех аргументов, которые использовались при запуске скрипта.

Итак, пока есть аргументы, тело сценария выполняется.

Тело цикла for всегда начинается с do и закрывается done, а между этими двумя ключевыми словами перечисляются команды, которые необходимо выполнить. Таким образом, пример сценария будет использовать echo для отображения значения каждого аргумента и останавливаться, когда больше нет доступных аргументов.

Давайте попробуем воспользоваться скриптом из листинга 2 в этом примере:

  1. Введите vi argument, чтобы создать файл argument и скопируйте содержимое из скрипта листинга 2 в этот файл.
  2. Сохраните файл и сделайте его исполняемым.
  3. Запустите команду ./argument a b c. Вы увидите, что отобразятся три строки.
  4. Запустите команду ./argument a b c d e f. Вы увидите, что помимо a b c отобразятся и d e f.

Переменные

Переменная — это метка, которая используется для обозначения определённого места в памяти, которое содержит определённое значение. Переменные могут быть определены статически с помощью NAME=value или динамическим способом. Существует два решения для динамического определения переменной:

  • Используйте ключевое слово read в скрипте, чтобы запросить данные у пользователя, запускающего скрипт.
  • Используйте подстановку команд, чтобы использовать результат команды и назначить его переменной. Например, команда date +%d-%m-%y показывает текущую дату в формате день-месяц-год. Чтобы сделать это в сценарии, вы можете использовать TODAY=$(date +%d-%m-%y). Для подстановки команд вам просто нужно поместить команду, результат которой вы хотите использовать, между скобками.

Листинг 3. Пример скрипта, использующего команду read

#!/bin/bash if [ -z $1 ]; then echo enter a text read TEXT else TEXT=$1 fi echo you have entered the text $TEXT exit 0 

В сценарии листинга 3 оператор if… then… else… fi используется для проверки существования аргумента $1. Это делается с помощью test (test — это отдельная команда). Команда test может быть написана двумя способами*: test или [… ]. В примере строка if [ -z $1 ] . выполняется, чтобы увидеть тест (проверку) -z $1.

* — на самом деле тремя источник (прим. переводчика)

-z test проверяет, существует или нет $1. Иначе говоря, строка if [ -z $1 ] проверяет, является ли $1 пустым, что означает, что при запуске этого сценария не было предоставлено никаких аргументов. Если это так, команды после оператора then выполняются.

Обратите внимание, что при написании команды test с квадратными скобками важно использовать пробелы после открывающей скобки и перед закрывающей скобкой, без пробелов команда не будет работать.

Обратите внимание, что оператор then следует сразу за test. Это возможно, потому что используется точка с запятой (;). Точка с запятой является разделителем команд и может заменить новую строку в скрипте.

В операторе then выполняются две команды: команда echo, которая отображает сообщение на экране, и команда read.

Команда read останавливает сценарий, чтобы пользовательский ввод мог быть обработан и сохранен в переменной TEXT. Поэтому read TEXT помещает все введённые пользователем данные в переменную TEXT, которая будет использоваться позже в скрипте.

Следующая часть представлена оператором else. Команды после оператора else выполняются во всех других случаях, что в данном случае означает «иначе, если аргумент был предоставлен». Если это так, то определяется переменная TEXT и ей присваивается текущее значение $1.

Обратите внимание, как определяется переменная: непосредственно после имени переменной стоит знак =, за которым следует $1. Обратите внимание, что вы никогда не должны использовать пробелы при определении переменных.

Затем условия if замыкается с помощью оператора fi. После завершения условия if вы точно знаете, что переменная TEXT определена и имеет значение. Предпоследняя строка скрипта считывает значение переменной TEXT и отображает это значение в STDOUT с помощью команды echo. Обратите внимание, что для запроса текущего значения переменной ссылается на имя переменной, начиная со знака $ перед ним.

Вы можете попрактиковаться на этом примере при работе с вводом.

  1. Откройте редактор и создайте файл с именем text. Введите содержимое кода из листинга 3 в этот файл.
  2. Запишите файл на диск и выполните chmod +x text, чтобы сделать его исполняемым.
  3. Запустите скрипт, выполнив ./text и без дополнительных аргументов. Вы увидите, что он запрашивает ввод.
  4. Запустите скрипт, используя «hello» в качестве аргумента (./text hello). Результат отобразит «you have entered the text hello» в STDOUT.

Использование условий и циклов

Как вы уже видели, в скрипте могут использоваться условные операторы. Эти условные операторы выполняются только в том случае, если определённое условие выполняется.

В bash есть несколько условных операторов и циклов, которые часто используются.

  • if… then… else — используется для выполнения кода, если определенное условие выполняется
  • for — используется для выполнения команд для диапазона значений
  • while — используется для выполнения кода, если выполняется определенное условие
  • before — используется для выполнения кода, пока не выполнено определенное условие
  • case — используется для оценки ограниченного количества конкретных значений

if then else

Конструкция if then else является общей для оценки конкретных условий. Вы уже видели пример с ним. Этот условный оператор часто используется вместе с командой test. Эта команда позволяет вам проверять многие вещи: например, не только, существует ли файл, но и сравнивать файлы, сравнивать целые числа и многое другое.

Подробнее о test можно узнать в справочнике командой man test.

Основная конструкция if есть if… then… fi.

Она сравнивает одно условие, как показано в следующем примере:

if [ -z $1 ] then echo no value provided fi 

В листинге 3 вы увидели, как можно оценить два условия, включая else в выражении. В листинге 4 показано, как можно оценить несколько условий от if до else. Это полезно, если нужно проверить много разных значений.

Обратите внимание, что в этом примере также используются несколько команд test.

Листинг 4. Пример с if then else

#!/bin/bash # run this script with one argument # the goal is to find out if the argument is a file or a directory if [ -f $1 ] then echo "$1 is a file" elif [ -d $1 ] then echo "$1 is a directory" else echo "I do not know what \$1 is" fi exit 0

|| и &&

Вместо написания полных операторов if… then вы можете использовать логические операторы || а также &&. || является логическим «ИЛИ» и выполнит вторую часть оператора, только если первая часть не верна; && является логическим «И» и выполнит вторую часть оператора только в том случае, если первая часть верна.

Рассмотрим эти две строки:

[ -z $1 ] && echo no argument provided
ping -c 1 8.8.8.8 2>/dev/null || echo node is not available 

В первом примере выполняется проверка, чтобы увидеть, пуст ли $1. Если эта проверка верна (что, в основном, означает, что команда завершается с кодом выхода 0), выполняется вторая команда.

Во втором примере команда ping используется для проверки доступности хоста.
В этом примере используется логическое «ИЛИ» для вывода текста «node is not available» в случае неудачной команды ping.

Вы обнаружите, что часто вместо условного оператора if будут использоваться && и ||. В упражнении ниже вы можете попрактиковаться в использовании условных операторов, используя либо if… then… else, либо && и ||.

Упражнение. Использование if… then… else

В этом упражнении вы поработаете над сценарием, который проверяет что является файлом, а что каталогом.

  1. Запустите редактор и создайте скрипт с именем filechk.
  2. Скопируйте содержимое из листинга 4 в этот скрипт.
  3. Запустите с ним пару тестов, такие как ./filechk /etc/hosts, ./filechck /usr, ./filechk non-existing-file.

Цикл for

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

#!/bin/bash # for (( COUNTER=100; COUNTER>1; COUNTER-- )); do echo $COUNTER done exit 0

Цикл for всегда начинается с ключевого слова for, за которым следует условие, которое необходимо проверить. Затем следует ключевое слово do, за которым следуют команды, которые должны быть выполнены, если условие истинно, завершается цикл с помощью ключевого слова done.

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

Маленькое пояснение

Внутри ((… )) вычисляются арифметические выражения и возвращается их результат. Например, в простейшем случае, конструкция a=$(( 5 + 3 )) присвоит переменной «a» значение выражения «5 + 3», или 8. Кроме того, двойные круглые скобки позволяют работать с переменными в стиле языка C.

Сначала переменная инициализируется значением 100, и до тех пор, пока значение больше 1, в каждой итерации вычитается 1. Пока условие истинно, значение переменной $COUNTER отображается с использованием команды echo.

В листинге 6 вы можете увидеть один из моих любимых однострочников с for. Диапазон определяется на этот раз как последовательность чисел, начиная со 100 и доходя до 104.

for i in ; do ping –c 1 192.168.4.$i >/dev/null && echo 192.168.4.$i is up; done 

Обратите внимание, как определяется диапазон: сначала вы указываете первое число, затем две точки и указываете последнее число в диапазоне. При этом с for i in для каждого из этих номеров присваивается переменная i. Каждое из этих чисел присваивается переменной i и затем выполняется команда ping, где опция -c 1 гарантирует, что отправляется только один запрос.

Результат выполнения команды ping не учитывается, поэтому её вывод перенаправляется в /dev/null. На основании состояния выхода команды ping выполняется часть выражения за &&. Таким образом, если хост доступен, отображается строка, указывающая, что он работает.

Понимание while и until

Если оператор for, о котором вы только что прочитали, полезен для работы с диапазонами элементов, то оператор while полезен тогда, когда вы хотите отслеживать что-то вроде доступности процесса. Также существует оператор until, который выполняется до тех пор, пока проверяемое условие ложно. В листинге 7 вы можете прочитать, как while используется для мониторинга активности процесса.

Примечание. Так и не понял, что делает этот скрипт. В моём случае используется CentOS 7 и по умолчанию там нет monitor, хотя в скрипте явно написано:

usage: monitor

Где-то пол часа гуглил для CetOS программу monitor, но так и не нашёл. И вообще не понятно каким тут боком monitor если используется ps aux. В любом случае так и не понял, что делает этот срипт. Большая просьба помочь решить этот вопрос, чтобы откорректировать текст и/или скрипт.

#!/bin/bash # # usage: monitor while ps aux | grep $1 | grep -v grep > /dev/tty11 do sleep 5 done clear echo your process has stopped logger $1 is no longer present mail -s "process $1 has stopped" root < .

Сценарий в листинге 7 состоит из двух частей. Во-первых, есть цикл while. Во-вторых, есть всё, что нужно выполнить, когда цикл while больше не оценивается как true.

Ядром цикла while является команда ps, которая имеет значение $1.

Обратите внимание на использование grep -v grep, который исключает строки, содержащие команду grep, из результата. Помните, что команда ps будет включать в себя все запущенные процессы, включая команду grep, которой передаётся вывод команды ps. Это может привести к ложному положительному совпадению.

Вывод команды ps aux перенаправляются в /dev/tty11. Это позволяет позже прочитать результаты из tty11, если это необходимо, но они не отображаются по умолчанию.

После операторов while следуют команды, которые необходимо выполнить, если проверяемое условие истинно. В данном случае это команда sleep 5, которая приостанавливает выполнение скрипта на 5 секунд.

Пока условие оператора while истинно, цикл продолжает выполняться. Если же условие ложно (что в данном случае означает, что процесс больше не доступен), то цикл останавливается и могут выполняться команды, следующие за ним.

* — по крайней мере в CentOS по умолчанию работает. (при. переводчика)

Обычно при использовании команды mail в интерактивном режиме открывается редактор, в котором можно написать тело сообщения. Этот редактор закрыт, предоставляя строку, которая имеет только точку. В этой команде точка предоставляется путём перенаправления STDIN. Это позволяет обрабатывать сообщение без каких-либо дополнительных требований к пользовательской активности.

Цикл while — это противоположность циклу until, пример которого приведён в листинге 8. until запускает итерацию, которая длится до тех пор, пока условие не станет истинным.

В листинге 8 он используется для фильтрации выходных данных команды users по вхождению $1, которое будет именем пользователя. Пока эта команда не верна, итерация продолжается. Когда имя пользователя найдено в выводе пользователей, итерация закрывается, и после цикла until выполняются остальные команды.

#!/bin/bash # until users | grep $1 > /dev/null do echo $1 is not logged in yet sleep 5 done echo $1 has just logged in mail -s "$1 has just logged in" root < .

Понимание case

Последний из важных итерационных циклов — это case*. Оператор case используется для оценки ряда ожидаемых значений. В частности, инструкции case важны в сценариях запуска Linux, которые в предыдущих версиях использовались для запуска служб.

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

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

case "$1" in start) start;; stop) rm -f $lockfile stop;; restart) restart;; reload) reload;; status) status ;; *) echo "Usage: $0 (start|stop|restart|reload|status)" ;; esac

case имеет несколько особенностей. Сначала идет строка — case последовательность in. Затем следует список всех возможных значений, которые необходимо оценить. Каждый элемент закрывается скобкой ).

Затем следует список команд, которые необходимо выполнить, если использовался конкретный аргумент. Список команд закрывается двойной точкой с запятой ;; может использоваться непосредственно после последней команды и может использоваться в отдельной строке.

Также обратите внимание, что *) относится ко всем другим параметрам, не указанным ранее. Это «всеохватывающий» оператор.

Цикл итераций case завершается оператором esac.

Обратите внимание, что последовательности в case выполняются по порядку. Когда будет выполнено первое совпадение, оператор case не будет ничего оценивать.

В рамках оценки могут использоваться шаблоны, подобные шаблону. Это показано в *) последовательности, которая соответствует всему. Но вы также можете использовать последовательности, такие как start|Start|START), чтобы соответствовать использованию другого case.

Отладка скриптов в Bash

Когда скрипт не делает того, что вы ожидаете, полезно выполнить некоторую отладку. Для начала, попробуйте выполнить его как аргумент команды bash -x. Это покажет вам построчно, что пытается сделать скрипт, а также конкретные ошибки, если он не работает как надо.

В листинге 10 показан пример использования bash -x, где сразу становится ясно, что команда grep не знает, что она должна делать, так как пропущен аргумент для её работы.

[root@server1 ~]# bash -x 319.sh + grep Usage: grep [OPTION]. PATTERN [FILE]. Try 'grep --help' for more information. + users + echo is not logged in yet is not logged in yet + sleep 5

Подводим итоги

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *