Quero armazenar tudo command args
em um array
, mas só funciona quando todos os argumentos não possuem nenhum ( *
).
Aqui está um teste sem usar array:
mkdir -p /tmp/hello
echo "hello" > /tmp/hello/hello.txt
echo "world" > /tmp/hello/world.txt
mkdir -p /tmp/world
# This cp command is success
cp /tmp/hello/* /tmp/world
Agora converta o comando cp em my_array:
mkdir -p /tmp/hello
echo "hello" > /tmp/hello/hello.txt
echo "world" > /tmp/hello/world.txt
mkdir -p /tmp/world
declare -a my_array=()
my_array[0]="cp"
my_array[1]="/tmp/hello/*"
my_array[2]="/tmp/world"
# Run the command and it will fail
"${my_array[@]}"
Aqui está o erro:
cp: cannot stat '/tmp/hello/*': No such file or directory
É possível usar ( *
) no my_array
? Qual é a sintaxe correta para implementar o ' cp /tmp/hello/* /tmp/world
' with my_array
?
Atualizar:
Existe um problem
from @choroba's answoer
, The $count
e $sedond_item
estará errado:
mkdir -p /tmp/hello
echo "hello" > /tmp/hello/hello.txt
echo "world" > /tmp/hello/world.txt
mkdir -p /tmp/world
my_array=(cp)
my_array+=(/tmp/hello/*)
my_array+=(/tmp/world)
count=${#my_array[@]}
printf "%s\n" "count is: $count"
sedond_item="${my_array[1]}"
printf "%s\n" "second item is: $sedond_item"
Aqui está o resultado da resposta de @choroba:
count is: 4
second item is: /tmp/hello/hello.txt
Mas os $count
e $sedond_item
estão corretos no meu array original:
mkdir -p /tmp/hello
echo "hello" > /tmp/hello/hello.txt
echo "world" > /tmp/hello/world.txt
mkdir -p /tmp/world
declare -a my_array=()
my_array[0]="cp"
my_array[1]="/tmp/hello/*"
my_array[2]="/tmp/world"
count=${#my_array[@]}
printf "%s\n" "count is: $count"
sedond_item="${my_array[1]}"
printf "%s\n" "second item is: $sedond_item"
Aqui está a saída do array original:
count is: 3
second item is: /tmp/hello/*
Eu não acho que você possa - com segurança 1 - fazer o que quiser no bash.
No bash (assim como no POSIX sh e ksh), aspas duplas de expansões de parâmetros (incluindo expansões de array como
"{my_array[@]}"
em shells que as suportam) evitam a divisão de palavras e a geração de nomes de arquivos (também conhecidos como globbing). Por outro lado, expansões sem aspas estão sujeitas tanto à divisão quanto ao globbing de palavras (a chamada operação "split+glob"). No bash, você pode desativar o globbing usandoset -f
(ou equivalenteset -o noglob
), mas no AFAIK você não pode desativar a divisão de palavras enquanto deixa o globbing ativado .Observe que a divisão de palavras ocorre antes do globbing, portanto, não é a expansão de
/path/without/whitespace/*
to/path/without/whitespace/name with whitespace
que é o problema - é a expansão de/path with whitespace/*
.No entanto, no zsh, as expansões de variáveis sem aspas não estão sujeitas a split+glob por padrão, e você pode ativar o globbing separadamente usando o
~
parâmetro expansion flag 2 .Observe que em todos os casos, o RHS de uma atribuição como
var=*
oumy_array[1]=/tmp/hello/*
não é expandido mesmo quando não está entre aspas.Assim, em zsh, por exemplo (observando que as matrizes zsh são indexadas de 1 e não de 0 como no bash):
(o mesmo que citado
"${my_array[@]}"
em bash e zsh), masVocê pode verificar se o globbing está acontecendo no momento da expansão adicionando outro arquivo:
Para o seu caso específico eu sugiro adicionar algumas medidas de segurança - em particular usar GNU mv's
-t
para definir explicitamente o diretório de destino e-n
evitar sobrescrições acidentais, e um argumento adicional--
para marcar o fim das opções (o que se torna importante se o globo puder se expandir para nomes de arquivos começando com hífens):Embora TBH, se você for mudar para zsh, considere usar o
zmv
módulo contribuído - que pega expressões glob (entre aspas) e as expande internamente - no lugar de plainmv
.com o que quero dizer sem usar
eval
embora observe que
${~my_array[@]}
o globbing é ativado durante a expansão de cada elemento da matrizUse o asterisco ao preencher o array, mas não preencha um elemento por vez:
Ou use o
+=
operador para expandir o array: