web-dev-qa-db-fra.com

Existe-t-il un moyen de faire échouer "mv" en silence?

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é?

63
Jonik

Vous cherchez cela?

$ mv  file dir/
mv: cannot stat ‘file’: No such file or directory
$ mv  file dir/ 2>/dev/null
# <---- Silent ----->
53
innocent-world

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.

14
Miroslav Koškár

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".

13
poige

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.

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 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

Dans ksh93:

files=(~(N)foo*)
((${#files[#]} == 0)) || mv -- "${files[@]}" ~/bar/

En bash:

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/
)

Dans yash

(
  set -o nullglob
  files=(foo*)
  [ "${#files[@]}" -eq 0 ] || mv -- "${files[@]}" ~/bar/
)

Dans 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/

POSIXly

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).

Plus de lecture:

6

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.)

3
a3nm

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/ \;
3
Matt

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

2
Valentin Bajrami

Vous pouvez tricher (de manière portable) avec Perl:

Perl -e 'system "mv foo* ~/bar/" if glob "foo*"'
2
Joseph R.

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 .)

2
Ilmari Karonen

Au lieu

mv foo* ~/bar/

tu peux faire

cp foo* ~/bar/
rm foo* 

Simple, lisible :)

1
Michał Króliczek

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}
0
Vincent Singha
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

0
Praveen Kumar BS