Est-ce que `xargs -t` sort stderr ou stdout, et pouvez-vous le contrôler?
-
20-08-2019 - |
Question
dis que j'ai un répertoire avec hi.txt et blah.txt et que j'exécute la commande suivante sur une ligne de commande linux-ish
ls *.* | xargs -t -i{} echo {}
la sortie que vous verrez est
echo blah.txt
blah.txt
echo hi.txt
hi.txt
je voudrais rediriger la sortie stderr (par exemple, "echo blah.txt" échoue ...), ne laissant que la sortie de la commande xargs -t écrite sur std out, mais on dirait que c’est aussi stderr.
ls *.* | xargs -t -i{} echo {} 2> /dev/null
Y a-t-il un moyen de le contrôler, de le sortir en sortie sur stdout?
La solution
Donc, je crois que ce que vous voulez, c'est d'avoir la sortie standard
- la sortie standard de l'utilitaire que xargs exécute
- la liste des commandes générées par xargs -t
Vous voulez ignorer le flux stderr généré par le utilitaire exécuté.
Corrigez-moi si je me trompe.
Premièrement, créons un meilleur utilitaire de test:
% cat myecho
#!/bin/sh
echo STDOUT $@
echo STDERR $@ 1>&2
% chmod +x myecho
% ./myecho hello world
STDOUT hello world
STDERR hello world
% ./myecho hello world >/dev/null
STDERR hello world
% ./myecho hello world 2>/dev/null
STDOUT hello world
%
Alors maintenant, nous avons quelque chose qui génère des sorties vers stdout et stderr, nous peut être sûr que nous obtenons seulement ce que nous voulons.
Une façon tangente de faire cela est de ne pas utiliser xargs, mais plutôt de faire. En écho à une commande et puis le faire est un peu ce que make fait. C'est son sac.
% cat Makefile
all: $(shell ls *.*)
$(shell ls): .FORCE
./myecho $@ 2>/dev/null
.FORCE:
% make
./myecho blah.txt 2>/dev/null
STDOUT blah.txt
./myecho hi.txt 2>/dev/null
STDOUT hi.txt
% make >/dev/null
%
Si vous êtes lié à l'utilisation de xargs, vous devez modifier votre utilitaire
xargs utilise donc il surpasse stderr. Ensuite, vous pouvez utiliser le 2>&1
tour d'autres
ont mentionné le déplacement de la liste de commandes générée par xargs -t à partir de stderr
to stdout.
% cat myecho2
#!/bin/sh
./myecho $@ 2>/dev/null
% chmod +x myecho2
% ./myecho2 hello world
STDOUT hello world
% ls *.* | xargs -t -i{} ./myecho2 {} 2>&1
./myecho blah.txt 2>/dev/null
STDOUT blah.txt
./myecho hi.txt 2>/dev/null
STDOUT hi.txt
% ls *.* | xargs -t -i{} ./myecho2 {} 2>&1 | tee >/dev/null
%
Cette approche fonctionne donc et réduit tout ce que vous voulez stdout (en laissant de côté ce que vous ne voulez pas).
Si vous vous trouvez souvent en train de faire cela, vous pouvez écrire un utilitaire général pour surpresser stderr:
% cat surpress_stderr
#!/bin/sh
$@ 2>/dev/null
% ./surpress_stderr ./myecho hello world
STDOUT hello world
% ls *.* | xargs -t -i{} ./surpress_stderr ./myecho {} 2>&1
./surpress_stderr ./myecho blah.txt 2>/dev/null
STDOUT blah.txt
./surpress_stderr ./myecho hi.txt 2>/dev/null
STDOUT hi.txt
%
Autres conseils
Utiliser:
ls | xargs -t -i{} echo {} 2>&1 >/dev/null
Le 2>&1
envoie l'erreur standard de xargs
à l'endroit où la sortie standard va actuellement; le >/dev/null
envoie la sortie standard d'origine à /dev/null
. Le résultat final est donc que la sortie standard contient les commandes echo et sed
contient les noms de fichier. Nous pouvons débattre des espaces dans les noms de fichiers et déterminer s'il serait plus simple d'utiliser un script -t
pour insérer "echo" au début de chaque ligne (sans option al
), ou si vous pouvez utiliser:
ls | xargs -i{} echo echo {}
(testé: Solaris 10, Korn Shell; devrait fonctionner sur les autres shells et les plateformes Unix.)
Si cela ne vous dérange pas de voir le fonctionnement interne des commandes, j'ai réussi à séparer la sortie d'erreur de 2>/tmp/xargs.stderr
et la sortie d'erreur de la commande exécutée.
al * zzz | xargs -t 2>/tmp/xargs.stderr -i{} ksh -c "ls -dl {} 2>&1"
La commande (non standard) /tmp/xargs.stderr
liste ses arguments un par ligne:
for arg in "$@"; do echo "$arg"; done
La première redirection (ksh -c "ls -dl {} 2>&1"
) envoie la sortie d'erreur de ls -ld
au fichier echo
. La commande exécutée est 'x1
', qui utilise le shell Korn pour exécuter x2
sur le nom du fichier. Toute sortie d'erreur passant à la sortie standard.
La sortie dans xxx
ressemble à:
ksh -c ls -dl x1 2>&1
ksh -c ls -dl x2 2>&1
ksh -c ls -dl xxx 2>&1
ksh -c ls -dl zzz 2>&1
J'ai utilisé 'zzz
' à la place de ksh -c "..."
pour vérifier que je testais les erreurs - les fichiers 1>&2
, <=> et <=> existaient, mais <=> n'existe pas.
La sortie sur la sortie standard ressemblait à:
-rw-r--r-- 1 jleffler rd 1020 May 9 13:05 x1
-rw-r--r-- 1 jleffler rd 1069 May 9 13:07 x2
-rw-r--r-- 1 jleffler rd 87 May 9 20:42 xxx
zzz: No such file or directory
Lorsqu’il est exécuté sans la commande encapsulée dans '<=>', la redirection des E / S a été transmise en tant qu’argument à la commande ('<=>'). Il a donc été signalé qu’il n’était pas possible de trouver le fichier '< => '. C’est-à-dire que <=> n’a pas utilisé le shell pour effectuer la redirection des E / S.
Il serait possible d'organiser diverses autres redirections, mais le problème fondamental est que <=> ne permet pas de séparer sa propre sortie d'erreur de celle des commandes qu'il exécute. Il est donc difficile à faire.
L’autre option assez évidente consiste à utiliser <=> pour écrire un script shell, puis le faire exécuter par le shell. C’est l’option que j’ai déjà montrée:
ls | xargs -i{} echo echo {} >/tmp/new.script
Vous pouvez alors voir les commandes avec:
cat /tmp/new.script
Vous pouvez exécuter les commandes pour supprimer les erreurs avec:
sh /tmp/new.script 2>/dev/null
Et si vous ne voulez pas voir la sortie standard des commandes, ajoutez <=> à la fin de la commande.
xargs -t
renvoie les commandes à exécuter à stderr avant de les exécuter. Si vous souhaitez qu’ils répercutent sur stderr, vous pouvez rediriger stderr vers stdout avec la construction 2>&1
:
ls *.* | xargs -t -i{} echo {} 2>&1
On dirait que xargs -t va dans stderr, et vous ne pouvez rien y faire.
Vous pourriez faire:
ls | xargs -t -i{} echo "Foo: {}" >stderr.txt | tee stderr.txt
pour n'afficher que les données stderr sur votre terminal pendant l'exécution de votre commande, puis passer de grep à stderr.txt après pour voir si quelque chose d'inattendu s'est produit, selon les lignes de grep -v Foo: stderr.txt
Notez également que sous Unix, ls *.*
n’affiche pas tout. Si vous souhaitez voir tous les fichiers, exécutez simplement ls
seul.
Si je comprends bien votre problème avec GNU Parallel, http://www.gnu.org/software/ parallel / ferait le bon choix:
ls *.* | parallel -v echo {} 2> /dev/null