esyr: (Default)
Вероятно, многие знают, что при помощи shell built-in read можно построчно читать всякое; особенно это актуально при считывании списка файлов, которые потенциально могут содержать пробелы (например, в выдаче ls или find) или чего-то подобного. Обычно это делается так:

Построчное чтение из файла:
while read line
do
  # code
done < file

Построчное чтение результата выполнения команды (вариант с перенаправлением):
ls | while read line
do
  # code
done

Вроде бы всё хорошо. Проблемы могут начаться в случае, когда во время чтения нужно изменять значения переменных. А именно, последний (и, кстати, наиболее часто используемый в случае, когда нужно обработать вывод команды, а не читать из файла) вариант приводит к созданию дочернего процесса (pipe же!), что приводит к тому, что все изменения переменных внутри тела цикла выполняются в подпроцессе и, как следствие, на процессе, в котором выполняется скрипт, не попадают. Это можно легко увидеть на следующем примере:
i=0; seq 1 3| while read line; do i=$(( $i + 1 )); done; echo $i
В bash и dash в результате будет выдан 0, в zsh и ksh (которые исполняют последний элемент пайплайна в текущем процессе в случае, если это shell built-in) — 3.
Обойти эту проблему можно несколькими способами. Один из них — использовать heredoc:
while read a b
do
  i=$(( $i + 1 ))
done <<EOF
`seq 1 3 | sed 's/^/1 /'`
EOF
echo $i
При этом, как можно видеть из примера выше, можно использовать произвольный пайплайн.
Очевидный недостаток этого решения — прежде, чем результат выполнения будет передан в цикл, он полностью будет получен. Это можно попытаться обойти, например, путём создания временного FIFO-файла:
fifofile=`mktemp`
rm "$fifofile"
mkfifo "$fifofile"

ls | sed 's/^/1 /' > "$fifofile" & # random command which output we need to parse
while read a b
do
  i=$(( $i + 1 ))
done < "$fifofile"
echo $i

rm "$fifofile"
Очевидно, что в данном случае строчка rm "$fifofile" будет выполнена после завершения цикла, что, по идее (так как read ждёт закрытия потока ввода), произойдёт только после завершения процесса, пишущего в FIFO и считывания из него всех данных.
Но всё же ощущается неаккуратность в виде наличия временных файлов. Тем более, что упражнение вида mktemp—rm—mkfifo теоретически может породить рейс. Можно попробовать соорудить конструкцию с созданием дескрипторов (like, exec 3<&0>&1):
exec 3<&0>&1

ls | sed 's/^/1 /' >&3 & # random command which output we need to parse
while read -u 3 a b
do
  i=$(( $i + 1 ))
done
echo $i

exec 3<&->&-

И, казалось бы, счастье есть: мы можем запихать произвольный пайплайн из процессов в этот дескриптор, а потом читать из него (или с помощью специального ключика у read, как в примере выше, или же просто <&3. Осталась одна маленькая проблема: если попытаться прибить скрипт до окончания его работы, то это ему ничуть не помешает. Посему, нужно повесить trap, который убивает дочерний процесс и завершает работу:
trap 'kill -9 $cpid; exit' TERM INT

exec 3<&0>&1

ls | sed 's/^/1 /' >&3 & # random command which output we need to parse
cpid=$!
while read -u 3 a b
do
  i=$(( $i + 1 ))
done
echo $i

exec 3<&->&-

Более аккуратного варианта как-то в голову не пришло. Если кто знает — поделитесь.

UPD. Таки приведённый вариант с дескрипторами работает только в zsh (не в dash). Буду думать, как его таки заимплементить.
esyr: (Default)
Во время написания очередного скипта у меня таки зачесалось и я решил вынести конфиг к нему отдельный файл. Естественно мне, как человеку ленивому, западло парсить текстовый файл (все парсеры у меня обычно на регекспах и исключительно монструозны, так что это было бы похлеще забивания гвоздей телескопом), посему логично было конфиг писать также на шелле (к тому же, это даёт пачку плюшек в виде прописывания substitusion-expansion-etc для значений параметров). Осталась одна проблема: как исполнить конфигурационный скрипт в текущем контексте? Просто выполнение вызывает субшелл, и, естественно, все изменения в нём никак не отражаются на текущем окружении. Первая идея с использованием бэктиков (<cat config<) потерпела провал: шелл считает, что после разворачивания должны быть команды; а это не так. Курение мана и попытка использовать фигурные скобки привела к тому же результату. Как оказалось, решение проще простого — существует встроенная команда eval, которая делает ровно то, что нужно, а именно, выполняет передаваемые ей параметры в текущем контексте. Результат выглядит следующим образом:
if [[ -f "$config_file" ]]
then
    eval $(cat "$config_file")
fi

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

Update. Таки да, поделие. Прогрессивное человечество пользуется командой source или .. Спасибо [livejournal.com profile] _winnie и [livejournal.com profile] v_for_vandal.
esyr: (Default)
Казалось бы, такая простая вещь, а решение неочевидно (по крайней мере мне), да.

readlink -f $filename

Спасибы ManMachine. Если бы не он, я бы его ещё час искал.

Update: Как мне тут Дима Чистиков подсказывает, для этого таки есть отдельная команда, realpath (1). За сие выношу ему отдельную благодарность.

PS. Псто этот здесь есть потому, что я уже раз n-ый натыкаюсь на эту проблему и не могу вспомнить, как она решается.
esyr: (Default)
Дружно ищем ошибки в следующих фразах:

«Права назначаются для каждого файла и каталога. Каталог рассматривается как файл, в котором хранится список файлов и некая дополнительная информация.»

«[пример шелл-скрипта] Выше рассмотрен пример не бинарной программы. Такие программы выполняет интерпретатор, указанный в первой строке файла с исходным кодом после комбинации символов #!. [...] По умолчанию для исполнения текстовых файлов используется /bin/sh.»

контекст: UNIX, шелл, права доступа.

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

Да, Лен, не подсказывай, а то неинтересно будет.

Profile

esyr: (Default)
esyr

October 2010

S M T W T F S
     12
3456789
10111213141516
17181920212223
24252627282930
31      

Syndicate

RSS Atom

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Jul. 20th, 2017 04:27 pm
Powered by Dreamwidth Studios