Une commande comme mv foo* ~/bar/
produit ce message dans stderr s'il n'y a aucun fichier correspondant foo*
.
mv: cannot stat `foo*': No such file or directory
Cependant, dans le script sur lequel je travaille, ce serait très bien, et j'aimerais omettre ce message de nos journaux.
Existe-t-il un moyen agréable de dire à mv
de se taire même si rien n'a été déplacé?
Vous cherchez cela?
$ mv file dir/
mv: cannot stat ‘file’: No such file or directory
$ mv file dir/ 2>/dev/null
# <---- Silent ----->
En fait, je ne pense pas que la mise en sourdine mv
soit une bonne approche (rappelez-vous que cela pourrait vous signaler également d'autres choses qui pourraient être intéressantes ... par exemple. Manquant ~/bar
). Vous ne voulez le couper que si votre expression glob ne retourne pas de résultats. En fait, ne l'exécutez pas du tout.
[ -n "$(shopt -s nullglob; echo foo*)" ] && mv foo* ~/bar/
Ne semble pas très attrayant et fonctionne uniquement dans bash
.
OR
[ 'foo*' = "$(echo foo*)" ] || mv foo* ~/bar/
sauf que vous êtes dans bash
avec nullglob
défini. Vous payez un prix de 3 fois la répétition du motif glob.
find . -maxdepth 1 -name 'foo*' -type f -print0 | xargs -0r mv -t ~/bar/
- mv
de GNU a l'option Nice "destination first" (-t
) et xargs
peuvent ignorer l'exécution de sa commande s'il n'y a aucune entrée (-r
). Utilisation de -print0
et -0
s'assure en conséquence qu'il n'y aura pas de gâchis lorsque les noms de fichiers contiennent des espaces et d'autres éléments "amusants".
Il est important de réaliser que c'est en fait le Shell qui se développe foo*
à la liste des noms de fichiers correspondants, il n'y a donc pas grand-chose que mv
puisse faire lui-même.
Le problème ici est que lorsqu'un glob ne correspond pas, certains shells comme bash
(et la plupart des autres shells de type Bourne, ce comportement de buggy a en fait été introduit par le Bourne Shell à la fin des années 70) passent le modèle textuellement. à la commande.
Alors ici, quand foo*
ne correspond à aucun fichier, au lieu d'interrompre la commande (comme les shells pré-Bourne et plusieurs shells modernes), le Shell passe un verbatim foo*
fichier vers mv
, donc en gros, demander à mv
de déplacer le fichier appelé foo*
.
Ce fichier n'existe pas. Si c'était le cas, cela aurait en fait correspondu au modèle, donc mv
signale une erreur. Si le motif avait été foo[xy]
à la place, mv
aurait pu déplacer accidentellement un fichier appelé foo[xy]
au lieu des fichiers foox
et fooy
.
Maintenant, même dans les shells qui n'ont pas ce problème (pre-Bourne, csh, tcsh, fish, zsh, bash -O failglob), vous obtiendrez toujours une erreur sur mv foo* ~/bar
, mais cette fois par le Shell.
Si vous voulez considérer que ce n'est pas une erreur s'il n'y a pas de fichier correspondant foo*
et dans ce cas, ne déplacez rien, vous voudrez d'abord construire la liste des fichiers (d'une manière qui ne provoque pas d'erreur comme en utilisant l'option nullglob
de certains shells), puis seul l'appel mv
est la liste n'est pas vide.
Ce serait mieux que de cacher toutes les erreurs de mv
(en ajoutant 2> /dev/null
serait) comme si mv
échouait pour une autre raison, vous voudriez probablement encore savoir pourquoi.
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 l'un de ces shells qui n'ont pas le bogue Bourne et signalent une erreur sans exécuter la commande lorsqu'un glob ne correspond pas (et l'option nullglob
n'a pas été activée) , donc ici, vous pouvez masquer l'erreur de zsh
et restaurer stderr pour mv
afin que vous puissiez toujours voir les erreurs mv
s'il y en a, mais pas l'erreur concernant la non globes correspondants:
(mv 2>&3 foo* ~/bar/) 3>&2 2>&-
Ou vous pouvez utiliser zargs
, ce qui éviterait également des problèmes si le foo*
glob s'étendrait aux fichiers trop man.
autoload zargs # best in ~/.zshrc
zargs -r -- foo* -- mv -t ~/bar # here assuming GNU mv for its -t option
files=(~(N)foo*)
((${#files[#]} == 0)) || mv -- "${files[@]}" ~/bar/
bash
n'a pas de syntaxe pour activer nullglob
pour un seul glob, et l'option failglob
annule nullglob
, vous aurez donc besoin de choses 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 doivent les enregistrer avant et les restaurer après.
(
shopt -s nullglob
shopt -u failglob
files=(foo*)
((${#files[@]} == 0)) || mv -- "${files[@]}" ~/bar/
)
yash
(
set -o nullglob
files=(foo*)
[ "${#files[@]}" -eq 0 ] || mv -- "${files[@]}" ~/bar/
)
fish
Dans le shell poisson, le comportement nullglob est la valeur par défaut pour la commande set
, donc c'est juste:
set files foo*
count $files > /dev/null; and mv -- $files ~/bar/
Il n'y a pas d'option nullglob
dans POSIX sh
et aucun tableau autre que les paramètres positionnels. 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 à la fois un foo[*]
et foo*
glob, on peut faire la différence entre le cas où il n'y a pas de fichier correspondant et celui où il y a un fichier qui se trouve être appelé foo*
(dont un set -- foo*
ne pouvait pas faire).
Je suppose que vous utilisez bash, car cette erreur dépend du comportement de bash pour étendre les globes inégalés à eux-mêmes. (Par comparaison, zsh génère une erreur lors de la tentative de développement d'un glob inégalé.)
Alors, qu'en est-il de la solution de contournement suivante?
ls -d foo* >/dev/null 2>&1 && mv foo* ~/bar/
Cela ignorera silencieusement le mv
if ls -d foo*
échoue, tout en enregistrant des erreurs si ls foo*
réussit mais le mv
échoue. (Il faut se méfier, ls foo*
pourrait échouer pour d'autres raisons que foo*
inexistant, par exemple, droits insuffisants, problème avec le FS, etc., de telles conditions seraient silencieusement ignorées par cette solution.)
Ce n'est probablement pas le meilleur mais vous pouvez utiliser la commande find
pour vérifier si le dossier est vide ou non:
find "foo*" -type f -exec mv {} ~/bar/ \;
Vous pouvez faire par exemple
mv 1>/dev/null 2>&1 foo* ~/bar/
ou mv foo* ~/bar/ 1&>2
Pour plus de détails, voir: http://mywiki.wooledge.org/BashFAQ/055
Vous pouvez tricher (de manière portable) avec Perl
:
Perl -e 'system "mv foo* ~/bar/" if glob "foo*"'
Si vous allez utiliser Perl, vous pouvez aussi bien aller jusqu'au bout:
#!/usr/bin/Perl
use strict;
use warnings;
use File::Copy;
my $target = "$ENV{HOME}/bar/";
foreach my $file (<foo*>) {
move $file, $target or warn "Error moving $file to $target: $!\n";
}
ou en une ligne:
Perl -MFile::Copy -E 'move $_, "$ENV{HOME}/bar/" or warn "$_: $!\n" for <foo*>'
(Pour plus de détails sur la commande move
, consultez la documentation de File :: Copy .)
Au lieu
mv foo* ~/bar/
tu peux faire
cp foo* ~/bar/
rm foo*
Simple, lisible :)
Ce code a fonctionné pour moi dans le cas d'un fichier, lorsque vous êtes à l'emplacement source.
[ -f ${fileName} ] && mv ${source}/${fileName} ${destination}
En cas de répertoire
[ -d ${source} ] && mv ${source} ${destination}
mv foo* ~/bar/ 2>/dev/null
Que la commande ci-dessus réussisse ou non, nous pouvons la trouver en quittant l'état de la commande précédente
command: echo $?
si sortie d'écho $? est différent de 0 signifie que la commande est infructueuse si la sortie est 0 signifie que la commande est réussie