
Представьте, что у меня есть две папки в моем текущем рабочем каталоге: 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
( p
rint w
orking d
irectory) — это команда, которая выводит содержимое $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)
синтаксис длямакросрасширения в Makefile
s, используемые командой make
.
В awk
языке, $
является унарным оператором для разыменования полей, и вы просто используете var
для ссылки на awk
переменную. Так что там, $(var)
будет то же самое, что $var
или $ var
и расширится до var
th поля (или всей записи, если var
равно 0).
В rc
оболочках типа , $(var)
то же самое, что $var
, $ var
, $ 'var'
, $ ( 'var' )
также будет работать, так как это список из одного элемента, переданного в $
, поэтому вы разыменовываете var
переменную, хотя обычно вы бы использовали просто $var
.
В TCL $(var)
будет расширяться до значения переменной массива с пустым именем для var
ключа:
$ tclsh
% array set {} {foo 1 bar 2}
% puts $(foo)
1
¹ Да, P
для P
rint не имеет особого смысла в этом контексте. Как вы можете догадаться, pwd
команда (из Unix V5 середины 70-х) была первой, и $PWD
илогичныйобработка текущего рабочего каталога, оба из которых были определены POSIX, появились позже (в ksh в начале 80-х, где pwd
на самом деле был псевдонимом для print - $PWD
(да, с отсутствующими -r
и кавычками!) и $PWD
имел аббревиатуру P
resent W
orking D
irectory в документации). tcsh
имеет переменную $cwd
( c
urrent w
orking d
irectory) для той же цели