Русский
Русский
English
Статистика
Реклама

Перевод Неожиданные подвохи при перенаправлениях оболочки в ((i))



Год назад вышла 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/bashi=1for f in *.txtdo  echo "Archiving $f as $i.txt"  cat template/header.txt "$f" > archive/$((i++)).txtdone

Опытные сценаристы уже наверняка забежали вперед и повторили это в своей оболочке, выяснив, что изменение будет работать, по крайней мере в Bash 5.0.16(1):

bash-5.0$ i=0; echo foo > $((i++)).txt; echo "$i" 1

Исходя из этого вы можете ожидать беглого просмотра истории коммитов Bash, и, быть может, призыва сохранять благосклонность к нашим обездоленным собратьям на macOS, использующим Bash 3.

Но нет. Вот демо-скрипт на той же системе:

bash-5.0$ ./demoArchiving chocolate_cake_recipe.txt as 1.txtArchiving emo_poems.txt as 1.txtArchiving 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 demoArchiving chocolate_cake_recipe.txt as 1.txtArchiving emo_poems.txt as 2.txtArchiving 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
К списку статей
Опубликовано: 04.05.2021 16:09:09
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Блог компании ruvds.com

Настройка linux

Системное администрирование

*nix

Ruvds_перевод

Bash

Linux

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru