Выходные данные переменной цикла показывают значение, отличное от ожидаемого

Выходные данные переменной цикла показывают значение, отличное от ожидаемого

Представьте, что у меня есть две папки в моем текущем рабочем каталоге: back/и front/. Я пытаюсь войти в каждую из них и сделать что-то. Из простого скрипта ниже:

for dir in */ ; do
    echo "$(dir)"
done

Я ожидал увидеть что-то вроде:

back
front

Однако вот что я получаю:

back  front
back  front

Я не понимаю. Следствием этого является то, что если я попытаюсь cdвойти в папку:

for dir in */ ; do
    echo "$(dir)"
    cd $(dir)
    echo "$(pwd)"
    cd ..
done

Я получил:

back  front
/path/to/back
back  front
/path/to/back

Т.е. я никогда не вхожу front/. Может кто-нибудь помочь мне понять, что я делаю не так?

решение1

В оболочках, подобных POSIX, $(cmd)синтаксис длязамена команды, $(cmd)расширяется до вывода за cmdвычетом конечных символов новой строки.

Вместо этого вы хотитерасширение параметра: $dirили ${dir}(здесь параметром являетсяdir переменная).

pwd( print working directory) — это команда, которая выводит содержимое $PWDспециальной переменной ¹, которая сама по себе содержит путь к текущему рабочему каталогу, поэтому $(pwd)расширяется до того же значения, что и , $PWDесли только $PWDне заканчивается символами новой строки.

Итак, вы хотите:

for dir in */; do 
  (
    printf '%s\n' "$dir"
    cd -- "$dir" &&
      printf '%s\n' "$PWD"
  )
done

Или:

for dir in */; do 
  (
    printf '%s\n' "$dir"
    cd -- "$dir" &&
      pwd
  )
done

Также помните, что

  • echoнельзя использовать для вывода произвольных данных, printfвместо этого используйте
  • вам следует обычно проверять статус выхода команд. Здесь непроверка вывода cdбудет означать, что следующее cd ..приведет вас в неправильное место, если первое cdне удалось.
  • лучше запустить cdв подоболочке (используя (...)), а не использовать cd .., поскольку последний не всегда гарантирует возврат в исходное место (если задействованы символические ссылки и/или некоторые компоненты пути были переименованы в интервале)
  • --необходимо использовать для отделения опций от неопций, когда нельзя гарантировать, что неопции не будут начинаться с -(или +что также может быть проблемой для некоторых команд).
  • В оболочках, подобных POSIX, расширения параметров и подстановки команд (и арифметические расширения) должны быть заключены в кавычки, иначе они подвергаются неявному split+glob (за исключением zsh), что обычно не требуется.

Также имейте в виду, что в bash (и других оболочках POSIX, кроме zsh), если шаблон не соответствует ни одному файлу, он остается неразвернутым.

Итак, если текущий рабочий каталог не содержит ни одного не скрытого файла, который можно определить как файл типакаталог, */шаблон глобуса просто расширится до самого себя */вместо пустого списка, и вы, скорее всего, увидите ошибку, сообщающую о cdтом, что он не может войти в этот */каталог.

В zshвы бы использовали Nквалификатор glob, как в for dir in */(N); do...(или for dir in *(N-/); do...чтобы избежать $dirокончания на /).

В кш93: for dir in ~(N)*/; do....

В bash, вы можете nullglobвременно включить эту опцию глобально:

shopt -s nullglob
for dir in */; do
  ...
done
shopt -u nullglob

Для полноты картины, $(var)синтаксис длямакросрасширения в Makefiles, используемые командой make.

В awkязыке, $является унарным оператором для разыменования полей, и вы просто используете varдля ссылки на awkпеременную. Так что там, $(var)будет то же самое, что $varили $ varи расширится до varth поля (или всей записи, если varравно 0).

В rcоболочках типа , $(var)то же самое, что $var, $ var, $ 'var', $ ( 'var' )также будет работать, так как это список из одного элемента, переданного в $, поэтому вы разыменовываете varпеременную, хотя обычно вы бы использовали просто $var.

В TCL $(var)будет расширяться до значения переменной массива с пустым именем для varключа:

$ tclsh
% array set {} {foo 1 bar 2}
% puts $(foo)
1

¹ Да, Pдля Print не имеет особого смысла в этом контексте. Как вы можете догадаться, pwdкоманда (из Unix V5 середины 70-х) была первой, и $PWDилогичныйобработка текущего рабочего каталога, оба из которых были определены POSIX, появились позже (в ksh в начале 80-х, где pwdна самом деле был псевдонимом для print - $PWD(да, с отсутствующими -rи кавычками!) и $PWDимел аббревиатуру Present Working Directory в документации). tcshимеет переменную $cwd( current working directory) для той же цели

Связанный контент