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?
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.
man xargs
dit --no-run-if-empty
.
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.
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 {} ';'
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 $@
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".