Не смотря на разнообразие различных языков некоторые задачи традиционно проще и быстрее решить используя возможности стандартного shell. Но даже использование этих возможностей требует понимания того, что происходит "внутри".
С одной из таких тонкостей использования shell хочется начать, а если статья покажется интересной общественности, то можно будет продолжить другими статьями на эту тематику.
Использование read
в скриптах бывает полезным когда необходимо прочитать строку из нескольких столбцов в разные переменные. При этом read
умеет возвращать ненулевой статус при достижении конца файла, что позволяет использовать его в конструкциях вида:
ls -lb | while read rights nlink user group other
do echo "$rights for $user:$group"
done
При этом без использования всяких awk
и cut
в переменную rights
попадут права доступа, а в переменные user
и group
- имя хозяина и группы. А что делать если нужны права только для одного файла и нет необходимости в использовании цикла?
Первым в голову приходит такой вариант решения:
ls -lb -- "$file" | read rights nlink user group other
echo "$rights for $user:$group"
Но выполнении такого скрипта нас будет поджидать неприятность. Переменные окажутся пустыми. Если вывод команды перенаправить во временный файл, и читать значения уже из него, то всё работает так как этого ожидается:
ls -lb -- "$file" > tmpfile
read rights nlink user group other < tmpfile
rm tmpfile
echo "$rights for $user:$group"
Но мы ведь не используем временные файлы?
Можно попытаться достичь нужного результата таким скриптом:
info="`ls -lb -- \"$file\"`"
rights="`echo \"$info\" | awk '{ print $1 }'`"
user="`echo \"$info\" | awk '{ print $3 }'`"
group="`echo \"$info\" | awk '{ print $4 }'`"
echo "$rights for $user:$group"
Но такая конструкция уже лишена изящества да и потребует трёх запусков awk
, что точно не ускорит работу скрипта.
Почему же не работает вариант с использованием read
и конвеера?
А причина банальна. Как это всем известно в *nix команды в конвеере запускаются параллельно, а это значит, что команда read
будет запущена в дочернем процессе и, соответственно, не сможет устанавливать переменные родительского процесса.
Поэтому, для использования read
в составе конвеера необходимо ограничивать область видимости прочитанных переменных и работать с ними только в рамках подпроцесса, в котором будет выполняться и сам read
:
ls -lb -- "$file" | {
read rights nlink user group other
echo "$rights for $user:$group"
}