bash->установка бита владельца директории из списка
Недавно пришлось решать немного странную задачку на BASH. Был список в текстовом формате:
группа=путь
Нужно было пройтись по каждому элементу списка и установить группу указанного в нём же.
На сервере стоял старый BASH, ассоциативных массивов, в котором ещё небыло.
Написал вот это:
#!/bin/bash if [ "$1" == "--help" ] || [ "$1" == "help" ] then echo "Использование: <имя скрипта> <путь до списка> <путь до лог-файла> Все параметры не обязательны. Если скрипт не получит путь до файла-списка - попытается открыть файл 'list' в своей директории. Тоже самое касается лог-файла."; exit 0; fi if [ -z "$2" ] then log="log.txt"; else log=$2; fi mess () { date=`date +%D-%r`; echo "[ $date] $1" >> $log; echo "[ $date] $1"; if [ "$2" -eq 1 ] then exit 1; fi } if [ -w "$log" ] then echo ""; else mess "Лог-файл не доступен для записи." 0; fi if [ -z "$1" ] then l="list"; else l=$1; fi list=$(cat $l); if [ -z "$list" ] then mess "Ошибка при открытии файла-списка!" 1; fi for i in $list; do group=`echo $i|cut -d "=" -f 1` dir=`echo $i|cut -d "=" -f 2` if `chgrp -R $group $dir` then mess "Установлена группа '$group' для дир. '$dir'" 0; else mess "Ошибка при обработке дир. '$dir'" 0; fi done
Скрипт по умолчанию считывает текстовый файл «list», лежащий рядом, в его директории. Если есть потребность указать файл из другой директории, вызывайте скрипт так:
script <путь-до-файл-листа> <путь-до-лог-файла>
Ещё можно посмотреть help-сообщение, командой script –help.
Разбиение строк на поля можно было сделать чуть элегантнее — при помощи временной смены IFS. Пример:
$ while IFS=’=’ read A B; do echo «A: $A; B: $B»; done
foo=bar
A: foo; B: bar
quux=xyzzy
A: quux; B: xyzzy
^D
Также круглые скобки после if непонятны — они организуют сабшелл. По-моему, достаточно было простого:
if chgrp -R $group $dir; then …
>Также круглые скобки после if непонятны
Обычное правило хорошего тона – разделение процессов между собой )
Так обычно делают, когда пишут, например на C. Тяжёлые процессы чаще всего отдаются процессу-потомку, чтобы операционная система смогла выдержать необходимый баланс ресурсов.
Спасибо за трюк, раньше так никогда не делал. Вообще задумка была реализовать explode() из PHP…
Вы что-то путаете. С этим кодом есть две проблемы (вторую я при первом просмотре не увидел).
Первая: выгрузка вычислений или других операций в другой процесс нужна только для того, чтобы освободить исходный процесс для *параллельного* решения другой задачи. У Вас же создаётся сабшелл, он исполняет скрипт «chgrp -R $group $dir» (точнее, вместо указанных переменных в скрипт будут подставлены их значения перед передачей), а исходный процесс в это время тупо спит в вызове waitpid() чтобы дождаться завершения порождённого процесса.
То есть Вы просто ухудшили ситуацию на один лишний процесс (шелл).
Код
if chgrp -R $group $dir; then …
делал бы то же самое, только без лишнего процесса.
Вторая проблема состоит в том, что Ваш код вообще делает не то, что вы предполагали, а именно:
1) Выполняется подстановка `chgrp -R $group $dir`. Сами по себе «бэктики» означают: «выполнить скрипт между ними и вернуть то, что скрипт написал на stdout».
2) Поскольку chgrp никогда ничего на stdout не пишет, этот скрипт вернёт пустую строку.
3) Затем выполнится код «()», т.к. в скобки будет поставлена пустая строка. Эта пустая строка будет скриптом, который выполняет сабшелл, организованный при помощи ().
Поскольку пустая строка — вполне валидный скрипт, это выполняется молча и всегда успешно. Демонстрация:
#!/bin/sh
touch /tmp/xyzzy
if (`chmod 0644 /tmp/xyzzy`); then
echo A
else
echo B
fi
if (`chmod 0644 /tmp/xyzzy; echo HEY`); then
echo A
else
echo B
fi
и выполнение:
$ /bin/dash /tmp/foo.sh
A
/tmp/foo.sh: 14: HEY: not found
B
Короче, как я указал выше, на самом деле Вам следует использовать простое
if chgrp -R $group $dir; then …
Спасибо огромное за подробные разъяснения, исправил скрипт.
Не знал про waitpid…