
Imagine que tenho duas pastas no meu diretório de trabalho atual: back/
e front/
. Estou tentando entrar em cada um deles e fazer algumas coisas. Do script simples abaixo:
for dir in */ ; do
echo "$(dir)"
done
Eu estava esperando uma saída como:
back
front
No entanto, o que estou recebendo é:
back front
back front
Eu não entendo. A consequência é que, se eu tentar cd
entrar na pasta:
for dir in */ ; do
echo "$(dir)"
cd $(dir)
echo "$(pwd)"
cd ..
done
Eu recebo:
back front
/path/to/back
back front
/path/to/back
Ou seja, eu nunca entro front/
. Alguém poderia me ajudar a entender o que estou fazendo de errado?
Responder1
Em shells do tipo POSIX, $(cmd)
a sintaxe parasubstituição de comando, $(cmd)
é expandido para a saída cmd
menos os caracteres de nova linha finais.
O que você quer em vez disso éexpansão de parâmetros: $dir
ou ${dir}
(aqui com o parâmetro sendo odir
variável).
pwd
( p
rint w
orking d
irectory) é o comando que gera o conteúdo da $PWD
¹ variável especial que contém um caminho para o diretório de trabalho atual, portanto, $(pwd)
expande para a mesma coisa, $PWD
desde que $PWD
não termine em caracteres de nova linha.
Então você quer:
for dir in */; do
(
printf '%s\n' "$dir"
cd -- "$dir" &&
printf '%s\n' "$PWD"
)
done
Ou:
for dir in */; do
(
printf '%s\n' "$dir"
cd -- "$dir" &&
pwd
)
done
Lembre-se também disso
echo
não pode ser usado para gerar dados arbitrários, useprintf
em vez disso- geralmente você deve verificar o status de saída dos comandos. Aqui, não verificar a saída de
cd
significaria que o seguintecd ..
o levaria ao lugar errado se o primeirocd
falhasse. - é melhor executar
cd
em um subshell (usando(...)
) em vez de usarcd ..
, pois o último nem sempre garante que você retorne ao local inicial (se houver links simbólicos envolvidos e/ou alguns componentes do caminho tiverem sido renomeados no intervalo) --
deve ser usado para separar opções de não opções quando não é possível garantir que as não opções não comecem-
(ou+
o que também pode ser um problema para alguns comandos).- em shells do tipo POSIX, expansões de parâmetros e substituições de comandos (e expansões aritméticas) devem ser citadas, caso contrário, elas passam por divisão + glob implícita (exceto em zsh), o que você geralmente não deseja.
Lembre-se também de que no bash (e em outros shells POSIX, exceto zsh), quando um glob não corresponde a nenhum arquivo, ele não é expandido.
Portanto, se o diretório de trabalho atual não contiver nenhum arquivo não oculto que possa ser determinado como sendo do tipodiretório, o */
padrão glob irá apenas se expandir para si mesmo */
em vez de uma lista vazia e você provavelmente verá um erro informando cd
que ele não pode entrar nesse */
diretório.
Em zsh
, você usaria o N
qualificador glob como em for dir in */(N); do...
(ou for dir in *(N-/); do...
para evitar $dir
terminar em /
).
Em ksh93 for dir in ~(N)*/; do...
:.
Em bash
, você pode definir a nullglob
opção globalmente temporariamente:
shopt -s nullglob
for dir in */; do
...
done
shopt -u nullglob
Para completar, $(var)
a sintaxe paramacroexpansões em Makefile
s conforme usado pelo make
comando.
Na awk
linguagem, $
é um operador unário para desreferenciar campos, e você usa apenas var
para se referir a uma awk
variável. Então, $(var)
seria o mesmo que $var
ou $ var
e expandiria para o var
campo (ou todo o registro se var
for 0).
Em rc
shells semelhantes a -, $(var)
o mesmo que $var
, $ var
, $ 'var'
, $ ( 'var' )
também funcionaria, já que é uma lista de um elemento passado para $
, então você está desreferenciando a var
variável, embora normalmente usaria apenas $var
.
No TCL, $(var)
expandiria para o valor da variável array com nome vazio para a var
chave:
$ tclsh
% array set {} {foo 1 bar 2}
% puts $(foo)
1
¹ Sim, P
para P
rint não faz muito sentido neste contexto. Como você pode imaginar, o pwd
comando (do Unix V5 em meados dos anos 70) foi o primeiro, e $PWD
ológicoo manuseio do diretório de trabalho atual, ambos especificados pelo POSIX, veio mais tarde (em ksh no início dos anos 80, onde pwd
na verdade era um alias para print - $PWD
(sim, com as faltas -r
e aspas!) E $PWD
foi bracronizado com o bracronyd P
resent W
orking D
irectory na documentação). tcsh
tem uma variável $cwd
( c
urrent w
orking d
irectory) para o mesmo propósito