[Настройка Linux, Системное администрирование, *nix] Неожиданные подвохи при перенаправлениях оболочки в $((i++)) (перевод)
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Год назад вышла ShellCheck v0.7.1. Главным образом в ней были подчищены и исправлены имеющиеся проверки, но также появились и новые. Лично меня из всех новинок больше всех удивила та, что указывает на проблему, обсуждение которой я еще нигде не встречал:
In demo line 6:
cat template/header.txt "$f" > archive/$((i++)).txt
^
SC2257: Arithmetic modifications in command redirections
may be discarded. Do them separately.
#Арифметические изменения при перенаправлениях в command могут быть
#отброшены. Выполняйте их отдельно
А вот весь скрипт:
#!/bin/bash
i=1
for f in *.txt
do
echo "Archiving $f as $i.txt"
cat template/header.txt "$f" > archive/$((i++)).txt
done
Опытные «сценаристы» уже наверняка забежали вперед и повторили это в своей оболочке, выяснив, что изменение будет работать, по крайней мере в Bash 5.0.16(1):
bash-5.0$ i=0; echo foo > $((i++)).txt; echo "$i"
1
Исходя из этого вы можете ожидать беглого просмотра истории коммитов Bash, и, быть может, призыва сохранять благосклонность к нашим обездоленным собратьям на macOS, использующим Bash 3.
Но нет. Вот демо-скрипт на той же системе:
bash-5.0$ ./demo
Archiving chocolate_cake_recipe.txt as 1.txt
Archiving emo_poems.txt as 1.txt
Archiving project_ideas.txt as 1.txt
То же самое верно для source ./demo, которая выполняет скрипт в том же экземпляре оболочки, где мы только что проводили проверку. Более того, происходит это только при перенаправлениях, но не в аргументах.
Так в чем же здесь дело?
Оказывается, что Bash, Ksh и BusyBox ash в процессе установки файловых дескрипторов также расширяют имя файла, из которого происходит перенаправление. Если вы знакомы с моделью процессов Unix, то псевдокод будет выглядеть так:
if command is external:
fork child process:
filename := expandString(command.stdout) # инкрементно увеличивает i
fd[1] := open(filename)
execve(command.executable, command.args)
else:
filename := expandString(command.stdout) # инкрементно увеличивает i
tmpFd := open(filename)
run_internal_command(command, stdout=tmpFD)
close(tmpFD)
Говоря иначе, область изменения переменной зависит от того, произвела ли оболочка ответвление нового процесса в ожидании выполнения команды.
Для встроенных команд, которые не разветвляются, например echo, это означает, что изменение произойдет в текущей оболочке. Именно такой тест мы и провели.
Для внешних же команд вроде cat изменение видимо только между моментом установки файлового дескриптора и вызовом команды для выполнения процесса. Это и делает демо-скрипт.
Конечно же, подоболочки хорошо известны опытным программистам, а также описаны в статье Why Bash is like that: Subshells. Но лично для меня это новый и, в частности, коварный их источник.
К примеру, этот скрипт отлично работает в busybox sh, где cat является встроенной:
$ busybox sh demo
Archiving chocolate_cake_recipe.txt as 1.txt
Archiving emo_poems.txt as 2.txt
Archiving project_ideas.txt as 3.txt
Аналогичным образом эта область может зависеть от того, переопределяли ли вы какие-либо команды функции-обертки:
awk() { gawk "$@"; }
# Инкрементирует
awk 'BEGIN {print "hi"; exit;}' > $((i++)).txt
# Не инкрементирует
gawk 'BEGIN {print "hi"; exit;}' > $((i++)).txt
Либо, если вы хотите переопределить псевдоним, то результат будет зависеть от того, использовали ли вы command или \:
# Инкрементирует
command git show . > $((i++)).txt
# Не инкрементирует
\git show . > $((i++)).txt
Чтобы избежать этой путаницы, обратите внимание на совет ShellCheck, и если при перенаправлении переменная является частью имени файла, то просто увеличивайте ее отдельно:
anything > "$((i++)).txt"
: $((i++))
Выражаю благодарность Strolls из #bash@Freenode за то, что указал на это поведение.
P.S. В процессе подготовки материала для статьи я выяснил, что dash всегда производит увеличение (хоть и с помощью $((i=i+1)), так как не поддерживает ++). ShellCheck v0.7.1 по-прежнему делает предупреждение, а код из master-ветки этого уже не делает.
оригинал
===========
Источник:
habr.com
===========
===========
Автор оригинала: Vidar
===========Похожие новости:
- [Настройка Linux, Разработка под Linux, История IT, Интервью] 30 лет Линукса. Интервью с Линусом Торвальдсом. Часть 1 (перевод)
- [Системное администрирование, Серверное администрирование, DevOps, Kubernetes] Как Asana использует Kubernetes (перевод)
- [Системное администрирование, Анализ и проектирование систем, Хранение данных, Стандарты связи, Распределённые системы] Идеальная избирательная система
- [Open source, *nix] FOSS News №68 – дайджест материалов о свободном и открытом ПО за 26 апреля – 2 мая 2021 года
- [Информационная безопасность, Разработка под Linux, Софт] Скрытое вредоносное ПО для бэкдора Linux обнаружили спустя три года
- [Системное администрирование, DevOps] Ansible playbook для управления Windows/Linux агентами Zabbix
- Первый тестовый выпуск дистрибутива Rocky Linux, идущего на смену CentOS
- [JavaScript, Программирование] Человеко-читаемый JavaScript: история о двух экспертах (перевод)
- [Настройка Linux, Системное администрирование] Ubuntu 16.04: Получаем обновления безопасности после окончания основной поддержки
- [Opera, *nix, Сетевые технологии, Браузеры, Софт] Открытые клиенты Hola VPN и Opera VPN
Теги для поиска: #_nastrojka_linux (Настройка Linux), #_sistemnoe_administrirovanie (Системное администрирование), #_*nix, #_ruvds_perevod (ruvds_перевод), #_bash, #_linux, #_blog_kompanii_ruvds.com (
Блог компании RUVDS.com
), #_nastrojka_linux (
Настройка Linux
), #_sistemnoe_administrirovanie (
Системное администрирование
), #_*nix
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 12:06
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Год назад вышла ShellCheck v0.7.1. Главным образом в ней были подчищены и исправлены имеющиеся проверки, но также появились и новые. Лично меня из всех новинок больше всех удивила та, что указывает на проблему, обсуждение которой я еще нигде не встречал: In demo line 6:
cat template/header.txt "$f" > archive/$((i++)).txt ^ SC2257: Arithmetic modifications in command redirections may be discarded. Do them separately. #Арифметические изменения при перенаправлениях в command могут быть #отброшены. Выполняйте их отдельно А вот весь скрипт: #!/bin/bash
i=1 for f in *.txt do echo "Archiving $f as $i.txt" cat template/header.txt "$f" > archive/$((i++)).txt done Опытные «сценаристы» уже наверняка забежали вперед и повторили это в своей оболочке, выяснив, что изменение будет работать, по крайней мере в Bash 5.0.16(1): bash-5.0$ i=0; echo foo > $((i++)).txt; echo "$i"
1 Исходя из этого вы можете ожидать беглого просмотра истории коммитов Bash, и, быть может, призыва сохранять благосклонность к нашим обездоленным собратьям на macOS, использующим Bash 3. Но нет. Вот демо-скрипт на той же системе: bash-5.0$ ./demo
Archiving chocolate_cake_recipe.txt as 1.txt Archiving emo_poems.txt as 1.txt Archiving project_ideas.txt as 1.txt То же самое верно для source ./demo, которая выполняет скрипт в том же экземпляре оболочки, где мы только что проводили проверку. Более того, происходит это только при перенаправлениях, но не в аргументах. Так в чем же здесь дело? Оказывается, что Bash, Ksh и BusyBox ash в процессе установки файловых дескрипторов также расширяют имя файла, из которого происходит перенаправление. Если вы знакомы с моделью процессов Unix, то псевдокод будет выглядеть так: if command is external:
fork child process: filename := expandString(command.stdout) # инкрементно увеличивает i fd[1] := open(filename) execve(command.executable, command.args) else: filename := expandString(command.stdout) # инкрементно увеличивает i tmpFd := open(filename) run_internal_command(command, stdout=tmpFD) close(tmpFD) Говоря иначе, область изменения переменной зависит от того, произвела ли оболочка ответвление нового процесса в ожидании выполнения команды. Для встроенных команд, которые не разветвляются, например echo, это означает, что изменение произойдет в текущей оболочке. Именно такой тест мы и провели. Для внешних же команд вроде cat изменение видимо только между моментом установки файлового дескриптора и вызовом команды для выполнения процесса. Это и делает демо-скрипт. Конечно же, подоболочки хорошо известны опытным программистам, а также описаны в статье Why Bash is like that: Subshells. Но лично для меня это новый и, в частности, коварный их источник. К примеру, этот скрипт отлично работает в busybox sh, где cat является встроенной: $ busybox sh demo
Archiving chocolate_cake_recipe.txt as 1.txt Archiving emo_poems.txt as 2.txt Archiving project_ideas.txt as 3.txt Аналогичным образом эта область может зависеть от того, переопределяли ли вы какие-либо команды функции-обертки: awk() { gawk "$@"; }
# Инкрементирует awk 'BEGIN {print "hi"; exit;}' > $((i++)).txt # Не инкрементирует gawk 'BEGIN {print "hi"; exit;}' > $((i++)).txt Либо, если вы хотите переопределить псевдоним, то результат будет зависеть от того, использовали ли вы command или \: # Инкрементирует
command git show . > $((i++)).txt # Не инкрементирует \git show . > $((i++)).txt Чтобы избежать этой путаницы, обратите внимание на совет ShellCheck, и если при перенаправлении переменная является частью имени файла, то просто увеличивайте ее отдельно: anything > "$((i++)).txt"
: $((i++)) Выражаю благодарность Strolls из #bash@Freenode за то, что указал на это поведение. P.S. В процессе подготовки материала для статьи я выяснил, что dash всегда производит увеличение (хоть и с помощью $((i=i+1)), так как не поддерживает ++). ShellCheck v0.7.1 по-прежнему делает предупреждение, а код из master-ветки этого уже не делает. оригинал =========== Источник: habr.com =========== =========== Автор оригинала: Vidar ===========Похожие новости:
Блог компании RUVDS.com ), #_nastrojka_linux ( Настройка Linux ), #_sistemnoe_administrirovanie ( Системное администрирование ), #_*nix |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 12:06
Часовой пояс: UTC + 5