Il est important de réaliser que c'est en fait le shell qui étend foo*
la liste des noms de fichiers correspondants, de sorte que rien ne mv
pourrait se faire.
Le problème ici est que, lorsqu'un glob ne correspond pas, certains shell ressemblent bash
(et la plupart des autres shell Bourne-like, ce comportement buggy a été introduit par le shell Bourne à la fin des années 70) et transmet le motif à la commande.
Donc ici, quand foo*
ne correspond à aucun fichier, au lieu d'abandonner la commande (comme le font les shells pre-Bourne et plusieurs shells modernes), le shell passe un foo*
fichier in extenso à mv
, demandant donc mv
de déplacer le fichier appelé foo*
.
Ce fichier n'existe pas. Si c'était le cas, il aurait correspondu au modèle, mv
signale donc une erreur. Si le motif avait été à la foo[xy]
place, vous mv
auriez pu déplacer accidentellement un fichier appelé à la foo[xy]
place des fichiers foox
et fooy
.
Maintenant, même dans les obus qui n'ont pas ce problème (pré-Bourne, csh, tcsh, poisson, zsh, bash -O failglob), vous obtiendrez toujours une erreur mv foo* ~/bar
, mais cette fois par l'obus.
Si vous voulez considérer que ce n'est pas une erreur s'il n'y a pas de correspondance de fichier foo*
et dans ce cas, ne déplacez rien, vous voudriez créer d'abord la liste des fichiers (d'une manière qui ne provoque pas d'erreur, comme en utilisant l' nullglob
option de certains shells), et alors seulement appeler si mv
la liste est non vide.
Ce serait mieux que de cacher toutes les erreurs de mv
(comme l'ajoutant 2> /dev/null
) comme si cela mv
échouait pour une autre raison, vous voudriez probablement encore savoir pourquoi.
en zsh
files=(foo*(N)) # where the N glob qualifier activates nullglob for that glob
(($#files == 0)) || mv -- $files ~/bar/
Ou utilisez une fonction anonyme pour éviter d'utiliser une variable temporaire:
() { (($# == 0)) || mv -- "$@" ~/bar/; } foo*(N)
zsh
est un de ces shells qui n’a pas le bogue Bourne et rapporte une erreur sans exécuter la commande quand un glob ne correspond pas (et que l’ nullglob
option n’a pas été activée), alors ici, vous pouvez masquer zsh
l’erreur et restaurer stderr pendant mv
si vous voyez toujours les mv
erreurs s'il y en a, mais pas l'erreur concernant les globs non correspondants:
(mv 2>&3 foo* ~/bar/) 3>&2 2>&-
Ou vous pourriez utiliser zargs
ce qui éviterait également les problèmes si le foo*
glob devenait trop volumineux.
autoload zargs # best in ~/.zshrc
zargs -r -- foo* -- mv -t ~/bar # here assuming GNU mv for its -t option
En ksh93:
files=(~(N)foo*)
((${#files[#]} == 0)) || mv -- "${files[@]}" ~/bar/
En bash:
bash
n'a pas de syntaxe à activer nullglob
pour un seul glob, et l' failglob
option est annulée nullglob
, vous aurez donc besoin d'éléments comme:
saved=$(shopt -p nullglob failglob) || true
shopt -s nullglob
shopt -u failglob
files=(foo*)
((${#files[@]} == 0)) || mv -- "${files[@]}" ~/bar/
eval "$saved"
ou définissez les options dans un sous-shell pour enregistrer devez les enregistrer avant et les restaurer ensuite.
(
shopt -s nullglob
shopt -u failglob
files=(foo*)
((${#files[@]} == 0)) || mv -- "${files[@]}" ~/bar/
)
Dans yash
(
set -o nullglob
files=(foo*)
[ "${#files[@]}" -eq 0 ] || mv -- "${files[@]}" ~/bar/
)
Dans fish
Dans la coquille de poisson, le comportement nullglob est le comportement par défaut de la set
commande, il s'agit donc simplement de:
set files foo*
count $files > /dev/null; and mv -- $files ~/bar/
POSIXly
Il n'y a aucune nullglob
option dans POSIX sh
et aucun tableau autre que les paramètres de position. Il existe une astuce que vous pouvez utiliser pour détecter si un glob correspond ou non:
set -- foo[*] foo*
if [ "$1$2" != 'foo[*]foo*' ]; then
shift
mv -- "$@" ~/bar/
fi
En utilisant a foo[*]
et foo*
glob, nous pouvons différencier le cas où il n'y a pas de fichier correspondant à celui où il y a un fichier appelé foo*
(ce qui set -- foo*
n'a pas pu être fait).
Plus de lecture:
mv foo* ~/bar/ 2> /dev/null
?