È importante rendersi conto che in realtà è la shell che lo espande foo*
all'elenco dei nomi di file corrispondenti, quindi c'è poco che mv
potrebbe fare da solo.
Il problema qui è che quando un glob non corrisponde, alcune shell come bash
(e la maggior parte delle altre shell Bourne-like, quel comportamento buggy è stato effettivamente introdotto dalla shell Bourne alla fine degli anni '70) passano il pattern alla lettera al comando.
Quindi qui, quando foo*
non corrisponde a nessun file, invece di interrompere il comando (come fanno le shell pre-Bourne e diverse shell moderne), la shell passa un foo*
file testuale a mv
, quindi sostanzialmente chiede mv
di spostare il file chiamato foo*
.
Quel file non esiste. In tal caso, avrebbe effettivamente adattato il modello, quindi mv
segnala un errore. Se il modello fosse stato foo[xy]
invece, mv
avrebbe potuto spostare accidentalmente un file chiamato al foo[xy]
posto dei file foox
e fooy
.
Ora, anche in quelle shell che non hanno questo problema (pre-Bourne, csh, tcsh, fish, zsh, bash -O failglob), avresti ancora un errore mv foo* ~/bar
, ma questa volta dalla shell.
Se vuoi considerarlo non un errore se non c'è corrispondenza dei file foo*
e in quel caso, non spostare nulla, ti consigliamo di costruire prima l'elenco dei file (in un modo che non causi un errore come usando l' nullglob
opzione di alcune shell), quindi solo call mv
è l'elenco non vuoto.
Sarebbe meglio che nascondere tutti gli errori di mv
(come 2> /dev/null
farebbe l' aggiunta ) come se mv
fallissero per qualsiasi altra ragione, probabilmente vorrai comunque sapere perché.
in zsh
files=(foo*(N)) # where the N glob qualifier activates nullglob for that glob
(($#files == 0)) || mv -- $files ~/bar/
Oppure usa una funzione anonima per evitare di usare una variabile temporanea:
() { (($# == 0)) || mv -- "$@" ~/bar/; } foo*(N)
zsh
è una di quelle shell che non hanno il bug Bourne e riportano un errore senza eseguire il comando quando un glob non corrisponde (e l' nullglob
opzione non è stata abilitata), quindi qui puoi nascondere zsh
l'errore e ripristinare stderr per mv
così vedresti ancora gli mv
eventuali errori, ma non l'errore relativo ai globs non corrispondenti:
(mv 2>&3 foo* ~/bar/) 3>&2 2>&-
Oppure potresti usare zargs
ciò che eviterebbe anche problemi se il foo*
glob si espandesse in file troppo man.
autoload zargs # best in ~/.zshrc
zargs -r -- foo* -- mv -t ~/bar # here assuming GNU mv for its -t option
In ksh93:
files=(~(N)foo*)
((${#files[#]} == 0)) || mv -- "${files[@]}" ~/bar/
In bash:
bash
non ha sintassi da abilitare nullglob
per un solo glob, e l' failglob
opzione annulla nullglob
quindi avresti bisogno di cose come:
saved=$(shopt -p nullglob failglob) || true
shopt -s nullglob
shopt -u failglob
files=(foo*)
((${#files[@]} == 0)) || mv -- "${files[@]}" ~/bar/
eval "$saved"
oppure impostare le opzioni in una subshell per salvare devono salvarle prima e ripristinarle successivamente.
(
shopt -s nullglob
shopt -u failglob
files=(foo*)
((${#files[@]} == 0)) || mv -- "${files[@]}" ~/bar/
)
Nel yash
(
set -o nullglob
files=(foo*)
[ "${#files[@]}" -eq 0 ] || mv -- "${files[@]}" ~/bar/
)
Nel fish
Nella shell di pesce, il comportamento nullglob è il valore predefinito per il set
comando, quindi è solo:
set files foo*
count $files > /dev/null; and mv -- $files ~/bar/
POSIXly
Non esiste alcuna nullglob
opzione in POSIX sh
e nessun array oltre ai parametri posizionali. C'è un trucco che puoi usare per rilevare se un glob corrisponde o meno:
set -- foo[*] foo*
if [ "$1$2" != 'foo[*]foo*' ]; then
shift
mv -- "$@" ~/bar/
fi
Usando sia a foo[*]
che foo*
glob, possiamo distinguere tra il caso in cui non esiste un file corrispondente e quello in cui esiste un file che sembra essere chiamato foo*
(cosa che set -- foo*
non si può fare).
Più lettura:
mv foo* ~/bar/ 2> /dev/null
?