É importante perceber que, na verdade, é o shell que o expande foo*
para a lista de nomes de arquivos correspondentes, para que haja pouco o mv
que fazer.
O problema aqui é que, quando um glob não corresponde, algumas conchas como bash
(e a maioria das conchas semelhantes a Bourne, esse comportamento de buggy foi realmente introduzido pelo shell Bourne no final dos anos 70) passam o padrão literalmente para o comando.
Portanto, aqui, quando foo*
não corresponde a nenhum arquivo, em vez de interromper o comando (como os shells pré-Bourne e vários shells modernos), o shell passa um foo*
arquivo literal para mv
, basicamente pedindo mv
para mover o arquivo chamado foo*
.
Esse arquivo não existe. Se isso acontecesse, ele realmente corresponderia ao padrão, então mv
relata um erro. Se o padrão tivesse sido foo[xy]
substituído, mv
poderia mover acidentalmente um arquivo chamado em foo[xy]
vez dos arquivos foox
e fooy
.
Agora, mesmo nos shells que não têm esse problema (pré-Bourne, csh, tcsh, fish, zsh, bash -O failglob), você ainda receberia um erro mv foo* ~/bar
, mas desta vez pelo shell.
Se você quiser considerar que não é um erro, se não houver nenhum arquivo correspondente foo*
e, nesse caso, não mover nada, você deve criar a lista de arquivos primeiro (de uma maneira que não cause erro, usando a nullglob
opção de algumas conchas) e, em seguida, apenas chamar mv
é a lista não está vazia.
Isso seria melhor do que ocultar todos os erros mv
(como adicionar 2> /dev/null
) como se mv
falhasse por qualquer outro motivo, você provavelmente ainda desejaria saber o motivo.
em zsh
files=(foo*(N)) # where the N glob qualifier activates nullglob for that glob
(($#files == 0)) || mv -- $files ~/bar/
Ou use uma função anônima para evitar o uso de uma variável temporária:
() { (($# == 0)) || mv -- "$@" ~/bar/; } foo*(N)
zsh
é um daqueles shells que não possuem o bug Bourne e relatam um erro sem executar o comando quando um glob não corresponde (e a nullglob
opção não foi ativada), então aqui, você pode ocultar zsh
o erro e restaurar stderr para mv
que você ainda veja os mv
erros, se houver, mas não o erro sobre os globs não correspondentes:
(mv 2>&3 foo* ~/bar/) 3>&2 2>&-
Ou você pode usar o zargs
que também evitaria problemas se a foo*
glob se expandisse para arquivos demais.
autoload zargs # best in ~/.zshrc
zargs -r -- foo* -- mv -t ~/bar # here assuming GNU mv for its -t option
No ksh93:
files=(~(N)foo*)
((${#files[#]} == 0)) || mv -- "${files[@]}" ~/bar/
Na festança:
bash
não possui sintaxe para ativar nullglob
apenas um glob, e a failglob
opção é cancelada nullglob
, sendo necessário itens como:
saved=$(shopt -p nullglob failglob) || true
shopt -s nullglob
shopt -u failglob
files=(foo*)
((${#files[@]} == 0)) || mv -- "${files[@]}" ~/bar/
eval "$saved"
ou defina as opções em um subshell para salvar, salve-as antes e restaure-as depois.
(
shopt -s nullglob
shopt -u failglob
files=(foo*)
((${#files[@]} == 0)) || mv -- "${files[@]}" ~/bar/
)
No yash
(
set -o nullglob
files=(foo*)
[ "${#files[@]}" -eq 0 ] || mv -- "${files[@]}" ~/bar/
)
No fish
No shell do peixe, o comportamento nullglob é o padrão para o set
comando, portanto, é apenas:
set files foo*
count $files > /dev/null; and mv -- $files ~/bar/
POSIXly
Não há nullglob
opção no POSIX sh
e nenhuma matriz além dos parâmetros posicionais. Existe um truque que você pode usar para detectar se um glob corresponde ou não:
set -- foo[*] foo*
if [ "$1$2" != 'foo[*]foo*' ]; then
shift
mv -- "$@" ~/bar/
fi
Usando a foo[*]
e foo*
glob, podemos diferenciar entre o caso em que não há arquivo correspondente e aquele em que há um arquivo que é chamado foo*
(o que set -- foo*
não poderia ser feito).
Mais leitura:
mv foo* ~/bar/ 2> /dev/null
?