Ключевые слова:shell, faq, bash, (найти похожие документы)
From: Vsevolod Stakhov <cebka[at]jet[dot]msk[dot]su>
Date: Mon, 3 Jan 2006 14:31:37 +0000 (UTC)
Subject: Shell FAQ
Оригинал: http://cebka.pp.ru/my/shell-faq.txt
> В чем состоят отличия различных оболочек?
Сравнительная таблица наиболее известных оболочек может быть найдена на
http://www.looking-glass.org/shell.html. Одной из наиболее функциональных
оболочек является zsh(хотя за функциональность приходится расплачиваться
некоторой потерей быстродействия). Для большинства операций обычно
достаточно bash или tcsh. Вообще, существует два типа оболочек -
произошедшие от Bourne Shell(sh, bash, ksh, zsh) и произошедшие от C
shell (csh, tcsh). Отличия в синтаксисе оболочек весьма существенны.
Оболочки, произошедшие от Bourne shell, чаще используются для
программирования, т.к. язык c-shell далёк от совершенства(хотя я бы не
сказал, что tcsh многим уступает в возможностях программирования
bash, но исторически csh обладал кучей огрехов, поэтому сейчас более
распространён синтаксис Bourne shell). Оболочки c-shell всегда являлись
больше интерактивными оболочками, но язык их скриптов намного ближе тем,
кто знаком с языком С. К сожалению, русской документации по C-shell я не
встречал. Может быть, в скором времени этот пробел будет заполнен.
> Существует ли руководство по bash на русском языке?
Да, есть: http://ln.com.ua/~openxs/projects/man/solaris8/bash.html
http://www.citforum.ru/programming/shell/index.shtml
> Как работают текстовые замены и подстановки в bash?
Вообще, текстовые замены и подстановки - это одна из самых мощных
возможностей bash(в оригинальной sh эти
возможности сильно урезаны). Существует несколько типов текстовых
подстановок командной строки:
фигурные скобки - всё, что находится в командной строке в фигурных
скобках {} расценивается как шаблон для
поиска, объединённый командой ИЛИ: file{1,2} заменится на file1 file2 chown
root /usr/{ucb/{ex,edit},lib/{ex?.?*,how_ex}
подстановка $. Все, что идёт после данного символа расценивается как
значение переменной, или результат выполнения команды. Также полезно
окружать подстановку после $ фигурными скобками(отделение от остальной
части текста): echo "${HOME} - this is where youк files are". echo ${`date`}
подстановка процессов
подстановка имён файлов
разбиение переменных на слова. По умолчанию интерпретатор разбивает все
переменные и результаты выполнения команд, не заключённые в двойные
кавычки, на слова, что позволяет обрабатывать переменные в циклах по
полям.
Существует достаточно много дополнительных параметров подстановки(например,
подстановка подстрок или установка переменной по умолчанию), но охватить
всё в рамках данного вопроса явно не удастся, за дополнительными сведениями
обращайтесь к документу http://ln.com.ua/~openxs/projects/man/solaris8/bash.html#expansion
> В чем отличие различных кавычек в shell?
Кавычки - это очень полезный инструмент. Различые виды кавычек применяются
в различных случаях(учтите, кавычки сохраняют своё значение и в
интерактивной оболочке). Итак, какие бывают кавычки:
двойные(") - внутри таких кавычек происходят все текстовые подстановки(см.
ниже), в том числе и подстановка обратных кавычек. Например echo "$PATH"
выведет значение переменной PATH, а echo "${PATH} `date`" выведет значение
PATH, поставит пробел и подставит результат выполнения программы date;
одинарные(') - внутри одинарных кавычек всё печатается, как есть, без
подстановок: echo '$PATH' выведет $PATH
обратные(`) - подставление вывода указанной в кавычках команды, также
выполняются все подстановки: `ps aux | grep $1` - выведет список заданных в
аргументе $1 процессов. Учтите, что результатом могут быть несколько строк.
> Как экранировать кавычки?
Вопрос заслуживает отдельного внимания, например, когда внутри кавычек есть
другие кавычки:
двойные(") - экранируются обратным слэшем, например echo "\"" выведет просто символ ";
одинарные(') - обратный слэш не спасает - приходится использовать
восьмеричный код символа 047: echo '\047$PATH\047' выведет '$PATH', в
Linux необходимо для echo указать опцию -е;
обратные(`) - экранируются аналогично двойным, т.е. обратным слэшем.
> Как выполнять арифметические действия над переменными?
Все переменные в shell являются строками, но существует возможность оценки
числовых выражений. Для объявления числовой переменной можно
воспользоваться командой let:
let a=6
let b=a*2
let c=a%4
let d=c/2
echo a=$a
echo b=$b
echo c=$c
echo d=$d
Результат работы:
a=6
b=12
c=2
d=1
Второй способ - оценка cтроковых выражений командой expr:
$a=2
$b=2
expr $a + $b
Выведет число 4. Заметьте, что просто $a + $b будет преобразовано в 2 + 2.
> Существует ли простой способ программно создать файл, содержащий несколько строк?
Да, существует. Это очень часто употребляемый прием для создания
многострочных файлов с возможностью подстановки переменных скрипта:
cat > cfg <<EOT
name = $a
And this file was generated with $EDITOR
many other lines...
...
EOT
> Как присвоить переменной результат выполнения команды?
Вопрос можно истолковать по-разному. Если имеется в виду присвоить
переменной код завершения программы, то используется следующий синтаксис:
./some_prog
RESULT=$?
На практике чаще используется проверка правильности выполнения последней
команды с помощью переменной $?(при успешном завершении обычно возвращается нуль):
./some_prog
if [ $? ne 0 ]
then
echo Bad exit code
exit
fi
У некоторых возникнет искушение сделать нечто подобное:
echo Error!!!
exit $?
Но, к сожалению, такой вариант всегда возвращает нуль, т.к. последней
командой было echo. В качестве решения проблемы можно присваивать код
завершения сомнительной программы некой переменной, а потом безбоязненно с
ней обращаться(учтите, операторы проверки [] также изменяют код
завершения).
Если же в данном вопросе имелся в виду не код завершения программы, а то
что было выведено программой в stdout, то необходимо заключить команду в
обратные кавычки:
DATE=`date`
> Как прочитать несколько строк с терминала?
Для этого используется цикл while с командой read. Стандартное решение:
while read line
do
echo $line
done
Можно выполнять построчное чтение не с терминала, а из файла - для этого
достаточно выполнить перенаправление цикла:
while read line
do
echo $line
done < /etc/passwd
C другой стороны для операции со строками лучше всего использовать awk.
Очень удобно использовать awk для
выбора некоторых слов из строки, но программирование в awk - это большая
отдельная тема.
> Как узнать, в командном или интерактивном режиме работает shell?
Можно проверить специальную переменную $- или переменную $PS1. Для первого
случая можно использовать скрипт, подобный такому:
case $- in
*i*) # Интерактивный режим
;;
*) # Неинтерактивный режим
;;
esac
Проверка режима оказывается полезной при написании сценариев, когда
неизвестно, необходимо ли выводить в stdout или stderr какие-либо
данные(нет смысла в неинтерактивном режиме). Также полезно применять в
инициализационных сценариях оболочек(например, чтобы выполнение отдельных
команд через ssh или rsh не вызывало запуск кучи вспомогательных программ).
Для оболочки C(csh, tcsh) синтаксис проверки будет несколько иным:
if(! $?prompt)
# Неинтерактивная оболочка
exit
else
# Интерактивная оболочка
echo Hi
exit
endif
> Что означают перенаправления в циклах?
Смысл такой: после каждого шага цикла значение переменной цикла считывается
или передаётся в конвейер. Пример удаления всех исполняемых файлов, если им
соответствует файлы .c(т.е. удаление объектных файлов):
for x in *; do [ -x $x -a -f $x.c ] && echo $x; done | xargs rm -f
Заметьте, что если последняя команда цикла ложна, то перенаправления
переменной цикла не происходит. В данном примере используется именно эта
возможность.
> А почему большинство скриптов написано на Bourne Shell? Почему моя любимая
> tcsh обойдена вниманием?
Скажу к слову, tcsh - это и моя любимая оболочка, я работаю в ней, как в
интерактивной, но скрипты всегда пишу в Bourne Shell. Причина проста:
скрипты на Bourne shell больше распространены, и любому администратору
необходимо знать эту оболочку, по ней есть много документации, в том числе
и на русском языке. Программировать в C-shell несколько привычнее, но
многочисленные ошибки реализации языка(особенно в клаccическом csh) сильно
затрудняют работу. В частности не очень удобен механизм перенаправлений:
sh:
mpg123 ./*.mp3 > /dev/null 2>&1
csh:
(mpg123 ./*.mp3 > /dev/null) >& /dev/null
В оболочке C практически невозможно нормально разделить stdout и stderr.
> Где можно найти полное описание встроенных переменных bash?
http://ln.com.ua/~openxs/projects/man/solaris8/bash.html#variables
> В моей системе нет команды killall. Что можно придумать?
Пишем примерно такой сценарий:
#!/bin/sh
kill $1 `ps ax | grep $2 | awk '{print $1}'| sed 's/\n/ /g'`
Вызываем скрипт так: killall -9 ftpd Один
минус: убивается больше процессов, чем нужно, т.к. grep находит сам себя и
пытается убить лишние процессы. Хотя это не смертельно, но если такого
процесса нет, то программа всё равно выводит Killed. Поэтому вывод надо
дополнительно фильтровать, но мне было вполне достаточно такого варианта.
> Как найти в /etc/passwd записи с нулевым UID(кроме root)?
#/bin/sh
for i in `awk -F: '{if($3 == "0" && $1 != "root") print $1}' /etc/passwd`
do
mail -s "Strange user!" root@mymail.net << EOT
There is a user ${i} that has uid = 0
`date`
EOT
done
Удобно добавить этот скрипт в crontab(учтите, что используется gawk -
параметр -F могут не понять старые версии awk, в таком случае внутри awk
скрипта надо установить FS=":").
> Как работать с интерактивными программами, например, telnet или ftp?
Вопрос не относится непосредственно к shell, для решения этой задачи удобно
использовать специальный пакет функций tcl - expect (http://expect.nist.gov).
Данный пакет содержит функции
spawn - породить процеcc
send - послать процессу входные данные
expect - в зависимости от выводимых данных послать процессу некие входные данные
Простой пример - получение файлов по ftp в автоматическом режиме:
spawn /usr/bin/ftp ftp.myhost.ru
expect{
"Name*: " {send "username\r"}
"Password:" {send "user_password\r"}
"failed" {puts "Can`t login."; exit 1}
}
send "lcd /etc\r"
expect "ftp> " {send "cd /pub/etc\r"}
expect "ftp> " {send "get passwd\r"}
expect "ftp> " {send "quit\r"}
exit 0
Работа с telnet и ssh также не должна вызвать затруднений(кроме автокоманд
на сервере, выполняемых при запуске командного интерпретатора). А вообще,
expect - это очень полезный инструмент, позволяющий автоматизировать
интерактивные операции(учтите, что для его работы необходим tcl).
> Будет ли продолжение данного FAQ?
Задавайте ваши вопросы мне: vsevolod at highsecure.ru. Наиболее
интересные войдут в этот список.
Сущиствует файлы на сервере скажим архивы и их требуется с одного сервака перекинуть на другой по сети можно ли написать в bash и закинуть скажем вв расписания что бы они сами скажем 4 раза в день копировались вне зависемости подключился админ или другой пользователь дан только ip и логин ?
Столкнулся с проблемой при выводе кавычек в команду:
есть переменная, которая выдает результат:
$moobj = "BSC23ZL:*:*:*", т.е. в кавычках, при попытке вставить переменную в команду:
temp="cnai export valid -i "${domain}"="${moobj}" "${attr_list}" -u YES -eemo YES -PG NO"
где ${domain} и ${attr_list} - просто текстовые переменные (буквы и запятые)...
дает результат при чтении переменной:
cnai export valid -i NREL="BSC23ZL:*:*:*" HIHYST,LOHYST, -u YES -eemo YES -PG NO
но при выводе этой переменной, как команды, дает:
cnai export valid -i 'NREL="BSC23ZL:*:*:*"' HIHYST,LOHYST, -u YES -eemo YES -PG NO
NREL="BSC23ZL:*:*:*" - выделяется '', из-за "", присутствующих в переменной $moobj и из-за этого команда не выполняется должным образом.
если при выводе сделать "$temp", то сама команда cnai (специфическая команда Ericsson) не определяется: