Estou em um diretório no qual tenho dois arquivos de texto:
$ touch test1.txt
$ touch test2.txt
Quando tento listar os arquivos (com o Bash) usando algum padrão, ele funciona:
$ ls test?.txt
test1.txt test2.txt
$ ls test{1,2}.txt
test1.txt test2.txt
No entanto, quando um padrão é produzido por um comando incluído $()
, apenas um dos padrões funciona:
$ ls $(echo 'test?.txt')
test1.txt test2.txt
$ ls $(echo 'test{1,2}.txt')
ls: cannot access test{1,2}.txt: No such file or directory
O que está acontecendo aqui? Por que o padrão {1,2}
não funciona?
bash
bash-expansion
Herosław Miraszewski
fonte
fonte
?
é citado e é expandido depois que o substitui, mas a expansão da chave não.$(...)
$
-expansions:zsh -o globsubst -c 'a=/e*; b={/b*,/v*}; echo $a; echo $b'
.Respostas:
É uma combinação de duas coisas. Primeiro, a expansão entre chaves não é um padrão que corresponde aos nomes dos arquivos: é uma substituição puramente textual - consulte Qual é a diferença entre `a [bc] d` (colchetes) e` a {b, c} d` (chaves)? . Segundo, quando você usa o resultado de uma substituição de comando fora de aspas duplas (
ls $(…)
), o que acontece é apenas a correspondência de padrões (e a divisão de palavras: o operador “split + glob”), não uma nova análise completa.Com
ls $(echo 'test?.txt')
, o comandoecho 'test?.txt'
gera a stringtest?.txt
(com uma nova linha final). A substituição do comando resulta na sequênciatest?.txt
(sem uma nova linha final, porque a substituição do comando retira as linhas novas à direita). Essa substituição não citada sofre divisão de palavras, produzindo uma lista que consiste na sequência única,test?.txt
pois não há caracteres de espaço em branco (mais precisamente, não há caracteres$IFS
) nela. Cada elemento dessa lista de um elemento passa por uma expansão curinga condicional e, como existe um caractere curinga?
na string, a expansão do curinga acontece. Como o padrãotest?.txt
corresponde a pelo menos um nome de arquivo, o elemento listtest?.txt
é substituído pela lista de nomes de arquivos que correspondem aos padrões, resultando na lista de dois elementos que contémtest1.txt
etest2.txt
. Finalmentels
é chamado com dois argumentostest1
etest2
.Com
ls $(echo 'test{1,2}')
, o comandoecho 'test{1,2}'
gera a stringtest{1,2}
(com uma nova linha final). A substituição do comando resulta na cadeiatest{1,2}
. Essa substituição não citada sofre divisão de palavras, resultando em uma lista que consiste na sequência únicatest{1,2}
. Cada elemento dessa lista de um elemento passa por uma expansão curinga condicional, o que não faz nada (o elemento é deixado como está), pois não há caractere curinga na sequência. Assimls
é chamado com o argumento únicotest{1,2}
.Para comparação, eis o que acontece com
ls $(echo test{1,2})
. O comandoecho test{1,2}
gera a stringtest1 test2
(com uma nova linha final). A substituição do comando resulta na sequênciatest1 test2
(sem uma nova linha final). Essa substituição sem aspas sofre divisão de palavras, produzindo duas cadeiastest1
etest2
. Então, como nenhuma das seqüências contém um caractere curinga, elas são deixadas sozinhas, por issols
é chamado com dois argumentostest1
etest2
.fonte
.txt
na segunda explicação.A expansão da cinta não ocorrerá após a substituição do comando. Você pode usar eval para forçar outra rodada de expansão:
O resultado é:
fonte
Esse problema é muito específico
bash
e é porque eles decidirambash
separar a expansão da chave da expansão do nome do arquivo (globbing) e executá-la primeiro, antes de todas as outras expansões.Na página de
bash
manual:No seu exemplo,
bash
você só verá seus aparelhos depois de executar a substituição de comando (the$(echo ...)
), quando for tarde demais.Isso é diferente de todos os outros invólucros, que executam a expansão da cinta logo antes (e alguns até como parte da) expansão do nome do caminho (globbing). Isso inclui, mas não se limita a,
csh
onde as expansões de braquetes foram inventadas pela primeira vez.O último exemplo, é a mesma em
csh
,zsh
,ksh93
,mksh
oufish
.Além disso, observe que a expansão do suporte como parte do globbing também está disponível através da
glob(3)
função de biblioteca (pelo menos no Linux e em todos os BSDs) e em outras implementações independentes (por exemplo, em perl:)perl -le 'print join " ", <test{1,2}.txt>'
.Por que isso foi feito de maneira diferente
bash
provavelmente tem uma história por trás disso, mas FWIW eu não consegui encontrar nenhuma explicação lógica e acho todas as racionalizações post-hoc não convincentes.fonte
perl
usado para invocarcsh
a expandir globs, por isso não é surpreendente que ele ainda reconhece os mesmos operadores englobamento comocsh
Tente por favor:::
ls $ (teste de eco {1,2} \. txt)
Com uma barra invertida. Funciona agora. Remova também o que o pôster anterior disse, as aspas. O ponto não é para o padrão correspondente, mas para ser considerado literalmente como Período aqui.
fonte
{1,2}
”se comporta dessa maneira? A pergunta não pergunta: "Como posso obter um comando{1,2}
para se comportar da maneira que o comando?
funciona?" Você está respondendo à pergunta errada. (2) A barra invertida não tem nada a ver com isso. Seu comando funciona da maneira que funciona porque você removeu as aspas que estavam no comando na pergunta.Funciona se você remover as aspas
fonte
?
).