web-dev-qa-db-fra.com

Comment ignorer les commandes xargs si l'entrée stdin est vide?

Considérez cette commande:

ls /mydir/*.txt | xargs chown root

L’intention est de changer les propriétaires de tous les fichiers texte dans mydir en root

Le problème est que s'il n'y a aucun fichier .txt dans mydir, alors xargs génère une erreur disant qu'aucun chemin n'est spécifié. Ceci est un exemple inoffensif car une erreur est générée, mais dans certains cas, comme dans le script que je dois utiliser ici, un chemin vide est supposé être le répertoire actuel. Donc, si j'exécute cette commande à partir de /home/tom/, s'il n'y a aucun résultat pour ls /mydir/*.txt et que tous les fichiers sous /home/tom/ ont leurs propriétaires modifiés en root.

Alors, comment puis-je faire en sorte que xargs ignore un résultat vide?

139
HyderA

Pour GNU xargs, vous pouvez utiliser l'option -r ou --no-run-if-empty:

--no-run-if-empty 
-r 
Si l'entrée standard ne contient pas de caractères non vides, n'exécutez pas la commande. Normalement, la commande est exécutée une fois, même s'il n'y a pas d'entrée. Cette option est une extension GNU.

233
Sven Marnach

man xargs dit --no-run-if-empty.

10
thiton

Les utilisateurs de xargs non GNU peuvent tirer avantage de -L <#lines>, -n <#args>, -i et -I <string>:

ls /empty_dir/ | xargs -n10 chown root # chown executed every 10 args or fewer
ls /empty_dir/ | xargs -L10 chown root # chown executed every 10 lines or fewer
ls /empty_dir/ | xargs -i cp {} {}.bak # every {} is replaced with the args from one input line
ls /empty_dir/ | xargs -I ARG cp ARG ARG.bak # like -i, with a user-specified placeholder

Gardez à l'esprit que xargs divise la ligne en blanc mais que les citations et les échappements sont disponibles; RTFM pour plus de détails.

10
arielCo

En termes de xargs, vous pouvez utiliser -r comme suggéré, mais il n’est pas pris en charge par BSD xargs.

Pour contourner le problème, vous pouvez transmettre un fichier temporaire supplémentaire, par exemple:

find /mydir -type f -name "*.txt" -print0 | xargs -0 chown root $(mktemp)

ou redirigez son stderr en null (2> /dev/null), par exemple.

find /mydir -type f -name "*.txt" -print0 | xargs -0 chown root 2> /dev/null || true

Une autre meilleure approche consiste à parcourir les fichiers trouvés à l’aide de la boucle while:

find /mydir -type f -name "*.txt" -print0 | while IFS= read -r -d '' file; do
  chown -v root "$file"
done

Voir aussi: Ignorer les résultats vides pour xargs dans Mac OS X


Veuillez également noter que votre méthode de modification des autorisations n’est pas excellente et qu’elle est découragée. Vous ne devez absolument pas analyser le résultat de la commande ls (voir: Pourquoi vous ne devez pas analyser le résultat de la commande ls ). En particulier lorsque vous exécutez votre commande par root, car vos fichiers peuvent contenir des caractères spéciaux pouvant être interprétés par le shell ou imaginer que le fichier ait un caractère espace autour de /, les résultats peuvent être terribles.

Par conséquent, vous devez modifier votre approche et utiliser à la place la commande find, par exemple.

find /mydir -type f -name "*.txt" -execdir chown root {} ';'
7
kenorb

Sous OSX: Réimplémentation Bash de xargs traitant de l’argument -r, mettez cela par exemple dans $HOME/bin et ajoutez-le à la PATH:

#!/bin/bash
stdin=$(cat <&0)
if [[ $1 == "-r" ]] || [[ $1 == "--no-run-if-empty" ]]
then
    # shift the arguments to get rid of the "-r" that is not valid on OSX
    shift
    # wc -l return some whitespaces, let's get rid of them with tr
    linecount=$(echo $stdin | grep -v "^$" | wc -l | tr -d '[:space:]') 
    if [ "x$linecount" = "x0" ]
    then
      exit 0
    fi
fi

# grep returns an error code for no matching lines, so only activate error checks from here
set -e
set -o pipefail
echo $stdin | /usr/bin/xargs $@
2
rcomblen

C'est un comportement de GNU xargs qui peut être supprimé en utilisant -r, --no-run-if-empty.

La variante * BSD de xargs a ce comportement par défaut, donc -r n'est pas nécessaire. Depuis FreeBSD 7.1 (sorti en janvier 2009), un argument est accepté (qui ne fait rien) pour des raisons de compatibilité.

Personnellement, je préfère utiliser longopts dans les scripts, mais puisque * BSD xargs n’utilise pas longopts, il suffit d’utiliser "-r" et xargs agira de la même manière sur * BSD et sur les systèmes Linux.

xargs sur MacOS (actuellement MacOS Mojave) ne supporte malheureusement pas l'argument "-r".

0
Mirko Steiner