Shell скрипты

]]>Основы]]> / ]]>awk]]> / ]]>sed]]> / ]]>Регулярные выражения]]> / ]]>Полезные команды]]>

Bourne shell (/bin/sh) присутствует во всех Unix системах, соответственно скрипты, написанные на этом языке, будут работать на любой Unix-машине. Для прочтения: man 1 sh. Незаменимая вещь при настройке и обслуживании сервера.

Основые понятия о shell скриптах

Переменные и аргументы командной строки

Присваивание значений переменным производится следующим образом: variable=value, получить присвоенное значение можно по ссылке $variable.

MESSAGE="Hello World"                        # Присвоить переменной строку "Hello World"
PI=3.1415                                    # Присвоить цифровое значение
N=8
TWON=`expr $N * 2`                           # Присвоить арифметическое выражение (только целые числа)
TWON=$(($N * 2))                             # Другой вариант
TWOPI=`echo "$PI * 2" | bc -l`               # Использование bc для операций с плавающей точкой
ZERO=`echo "c($PI/4)-sqrt(2)/2" | bc -l`

Аргументы командной строки:

$0, $1, $2, ...                              # $0 - сама команда(название скрипта) 
$#                                           # Кол-во аргументов командной строки
$*                                           # Все аргументы (аналог $@)

Специальные переменные

$$                                           # ID текущего процесса
$?                                           # Код возврата последней выполненной команды
  command
  if [ $? != 0 ]; then
    echo "command failed"
  fi
mypath=`pwd`
mypath=${mypath}/file.txt
echo ${mypath##*/}                           # Вывести только имя файла
echo ${mypath%%.*}                           # Полный путь без расширения
var2=${var:=string}                          # Если var назначена, использовать ее значение, иначе string
# значение string будет присвоено и var и var2.

Конструкции

for file in `ls`
do
    echo $file
done

count=0
while [ $count -lt 5 ]; do
    echo $count
    sleep 1
    count=$(($count + 1))
done

myfunction() {
    find . -type f -name "*.$1" -print       # $1 -первый аргумент функции
}
myfunction "txt"

Простенький скрипт генерирующий файл

MYHOME=/home/colin
cat > testhome.sh << _EOF   # Все до символа _EOF, записывается в файл testhome.sh
if [ -d "$MYHOME" ] ; then
    echo $MYHOME exists
else
    echo $MYHOME does not exist
fi
_EOF
sh testhome.sh

Еще один пример скрипта на bourne shell

В данном примере, скрипт создает PDF буклет, из XHTML документа:

#!/bin/sh
# Данный скрипт создает книгу в формате PDF, для печати на принтере
if [ $# -ne 1 ]; then                        # Проверить аргумент
  echo 1>&2 "Usage: $0 HtmlFile"
  exit 1                                     # Выход с кодом больше нуля в случае ошибки
fi

file=$1                                      # Присвоить имя файла из агрумента
fname=${file%.*}                             # Взять только имя файла
fext=${file#*.}                              # Взять только расширение

prince $file -o $fname.pdf                   
pdftops -paper A4 -noshrink $fname.pdf $fname.ps # Создать postscript буклет
cat $fname.ps |psbook|psnup -Pa4 -2 |pstops -b "2:0,1U(21cm,29.7cm)" > $fname.book.ps

ps2pdf13 -sPAPERSIZE=a4 -sAutoRotatePages=None $fname.book.ps $fname.book.pdf
# В Windows используйте #a4 и #None !
exit 0                                       # Выход с кодом успешного завершения

awk

Awk - это весьма мощьный и полезный язык для обработки текстовой информации. Кучу примеров, без труда можно найти в сети, здесь приведены лишь несколько простых:

awk '{ print $2, $1 }' file                  # Вывести из файла 2 колонки, поменя из местами
awk '{printf("%5d : %s\n", NR,$0)}' file     # Форматирование вывода с номерами строк
awk '{print FNR "\t" $0}' files              # Несколько измененный вариант
awk NF test.txt                              # Удалить из вывода пустые строки (аналогично grep '.')
awk 'length > 80'                            # Напечатать строки, длинной более 80 символов

sed

Sed - это неинтерактивный строчный редактор, принимает текст с устройства stdin или из текстового файла, выполняет некоторые со строками и выводит в stdout или в файл. Часто применяется в конвейерной обработке данных совместно с другими командами.

sed 's/string1/string2/g'                    # Заменить string1 на string2
cat ./wrong.txt | sed 's/wrong/right/g' > ./right.txt              # Вывести содержимое файла, заменить слова и записать в другой файл
sed 's/\(.*\)1/\12/g'                        # Модифицировать "строку1" в "строку2"
sed '//,/<\/p>/d' t.xhtml                 # Удалить строки, начинающиеся с ⁢p>
                                             # И заканчивающиеся </p>
sed '/ *#/d; /^ *$/d'                        # Удалить комментарии и пустые строки
sed 's/[ \t]*$//'                            # Удалить символы табуляции
sed 's/^[ \t]*//;s/[ \t]*$//'                # Удалить пробелы в начале и конце
sed 's/[^*]/[&]/'                            # Заключить первый символ в квадратные скобки
sed = file | sed 'N;s/\n/\t/'                # Порядковый номер в каждой строке

regex - регулярные выражения

[\^$.|?*+()                          # Специальные символы, остальные символы означают самих себя
\                                    # Экранирует специальные символы
*                                    # Повтор 0 или 1 раз
.                                    # Любой символ, за исключением символа новой строки
.*                                   # Совпадает 0 или более символов
^                                    # Начало строки
$                                    # Конец строки
.$                                   # Совпадает с одним любым символ в конце строки
^ $                                  # Совпадает со строкой, состоящей из одного пробела
[^A-Z]                               # Любые символы, не входящие в диапазон от А до Z

Некоторые полезные команды

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

sort -t. -k1,1n -k2,2n -k3,3n -k4,4n         # Отсортировать IPv4 ip адреса
echo 'Test' | tr '[:lower:]' '[:upper:]'     # Смена регистра символов
echo foo.bar | cut -d . -f 1                 # Вернет foo
PID=$(ps | grep script.sh | grep bin | awk '{print $1}')    # PID запущенного скрипта
PID=$(ps axww | grep [p]ing | awk '{print $1}')             # PID процесса ping
IP=$(ifconfig $INTERFACE | sed '/.*inet addr:/!d;s///;s/ .*//')   # Linux
IP=$(ifconfig $INTERFACE | sed '/.*inet /!d;s///;s/ .*//')        # FreeBSD
if [ `diff file1 file2 | wc -l` != 0 ]; then [...] fi       # Файл изменен?
cat /etc/master.passwd | grep -v root | grep -v \*: | awk -F":" \ # Создание файла паролей http passwd
'{ printf("%s:%s\n", $1, $2) }' > /usr/local/etc/apache2/passwd

testuser=$(cat /usr/local/etc/apache2/passwd | grep -v \    # Проверить пользователя в passwd
root | grep -v \*: | awk -F":" '{ printf("%s\n", $1) }' | grep ^user$)
:(){ :|:& };:                                # bash fork bomb :). Убьет вашу машину ))
tail +2 file > file2                         # Удалить первую строку из файла

Я использую этот небольшой трюк, что-бы разом изменить расширение для кучи файлов, например с *.cxx на *.cpp. (советую для начала протемтировать данный пример, без | sh в конце строки.
Тоже самое можно смделать с помощью команды rename, если она присутствует, или с помошью встроенных средст оболочки.

# ls *.cxx | awk -F. '{print "mv "$0" "$1".cpp"}' | sh
# ls *.c | sed "s/.*/cp & &.$(date "+%Y%m%d")/" | sh # Копировать файлы *.c в *.c.20080401
# rename .cxx .cpp *.cxx                             # Переименовать все файлы .cxx в cpp
# for i in *.cxx; do mv $i ${i%%.cxx}.cpp; done      # Встроенными средствами