1
votes

Code shell le plus court pour exécuter à la fois traceroute et traceroute6

Je voudrais exécuter à la fois traceroute -w2 et traceroute6 -w2 , séquentiellement, dans un script shell, pour essayer plusieurs hôtes différents.

Une approche naïve peut simplement utiliser une variable temporaire pour rassembler tous les hôtes à l'intérieur (par exemple, définissez HOSTS sur ordns.he.net one.one.one.one google-public- dns-a.google.com ), puis dirigez-le simplement individuellement vers chaque commande, comme, echo $ HOSTS | xargs -n1 traceroute -w2 et al, mais cela fonctionnerait différemment dans tcsh que dans bash , et peut être sujet à des erreurs si vous voulez en ajouter plus commandes (car vous les ajouteriez sous forme de code plutôt que sous forme de liste de choses à faire), et je pense qu'il existe un meilleur moyen de regrouper la liste de commandes (par exemple, un nom de commande avec un seul paramètre) avec la liste d'arguments (par exemple, les noms d'hôte dans notre exemple), pour que le shell exécute toutes les combinaisons possibles.

J'ai essayé de faire une combinaison de xargs -n1 (pour les hôtes) et xargs -n2 (pour les commandes avec un paramètre) qui se raccordent, mais cela n'avait pas vraiment de sens et n'a pas fonctionné.

Je recherche une solution qui n'utilise aucun outil GNU et qui fonctionnerait dans une installation de base d'OpenBSD (si nécessaire, perl fait partie de l'OpenBSD de base, donc, il est disponible en tant que bien).


5 commentaires

Pourquoi tcsh est-il impliqué dans cette question alors qu'OpenBSD a un sh POSIX parfaitement bon? csh est une relique de l'époque où, avant POSIX (c'est-à-dire avant 1992), Bourne avait un ensemble de fonctionnalités inadéquat, et ksh était sous licence commerciale et coûtait de l'argent; aucune de ces choses n'est vraie depuis des décennies.


... problème plus important: l'optimisation de la concision plutôt que de l'exactitude dans les langages shell est une mauvaise idée en général. Il y a beaucoup de bagages historiques en coquille - à peu près la famille complète des langues inspirées par ou essayant d'être rétrocompatibles avec n'importe quel obus des années 1970 - et le contourner rend généralement une certaine quantité de bagages obligatoire.


@CharlesDuffy c'est parce que j'utilise tcsh comme shell principal (j'aime la valeur par défaut de FreeBSD de history-search-backward pour bindkey -k up sur mes systèmes), et c'est destiné à être un extrait de mon shell


@cnst J'utilise aussi tcsh comme interpréteur de ligne de commande, pour quelques commodités pour le travail quotidien, dont vous parlez (je le lie à Ctrl-P). (Mais j'envisage de passer à bash pour ses nombreux autres avantages.) Mais alors j'ai un ~ / bin où il y a divers utilitaires, et ce sont principalement scripts bash (et quelques-uns en Perl, et quelques programmes C fonctionnant via les fonctionnalités shell precmd et period ). Vous n'êtes pas lié à tcsh pour cela, et cela ressemble à quelque chose qui pourrait être fait dans un joli script li'l.


La solution simple à «certaines choses ne sont pas compatibles avec tcsh» est de ne pas utiliser tcsh.


3 Réponses :


4
votes

Rester simple:

#!/bin/sh
set -- host1 host2 host3 host4 ...
for host do traceroute -w2 -- "$host"; done
for host do traceroute6 -w2 -- "$host"; done


10 commentaires

Ce n'est pas une réponse, car, 1, cela nécessite sh , et, 2, il est déjà connu comme faisant partie de la question. De plus, ce n'est guère un extrait de code qui pourrait être copié-collé dans le shell, ce qui est l'objet de la question.


La balise shell , que vous avez utilisée, est pour POSIX sh (qui, selon vos spécifications, OpenBSD est livré avec). J'ai lu et relu la question et je ne vois aucune preuve de (2).


Je suppose qu'il ne vous a jamais appris ne pas regarder gift-horse dans la bouche ...


@cnst C'est très certainement une réponse (et une bonne, comme indication de la voie à suivre, en ce qui me concerne). Vous avez tout à fait le droit de ne pas l'aimer, bien sûr, mais votre réponse est impolie.


@CharlesDuffy, comme pour (2), je ne vois pas en quoi votre solution est différente du echo $ HOSTS | xargs -n1 traceroute -w2 qui est présent dans la question; en fait, vous n'expliquez jamais à quoi sert - , et je pense que cela a amené l'autre réponse à utiliser le - sans raison particulière également.


@cnst, - est un sigil de fin d'options, tel que normalisé dans Instructions de syntaxe de l'utilitaire POSIX # 10: Le premier argument - qui n'est pas un argument-option doit être accepté comme un délimiteur indiquant la fin des options . Tous les arguments suivants doivent être traités comme des opérandes, même s'ils commencent par le caractère - . - ainsi, cela empêche un nom d'hôte commençant par un tiret d'être traité comme une collection de simples- options de caractères pour traceroute .


@cnst, ... si votre copie de traceroute ne le prend pas en charge, alors il ne respecte pas la norme correspondante, ce qui devrait être signalé en amont comme un bogue.


@cnst, ... et utilisation de echo $ HOSTS | xargs est bogué, point final. Vous obtenez une expansion globale indésirable (f / e, foo [bar] dans les entrées est comparée aux noms de fichiers locaux et donc potentiellement remplacée par foob , fooa , etc; et peut également déclencher des échecs complets si globfail est défini, ou disparaître silencieusement lorsqu'il est exécuté sur un shell avec l'option nullglob ) Vous obtenez également des xargs qui effectuent une analyse pas tout à fait compatible avec le shell des guillemets et d'autres caractères de vos chaînes, ainsi que diverses bizarreries qui en découlent, vous ne pouvez donc pas l'utiliser en toute sécurité pour un contenu arbitraire.


@cnst, ... voir BashPitfalls # 56 et BashPitfalls # 14 .


@CharlesDuffy ce seraient tous des points très valables, mais pas dans ce contexte. Cependant, merci de m'avoir appris - - On m'a demandé plusieurs fois de supprimer des fichiers comme -rf dans diverses interviews, et ma seule solution était toujours . / -rf , et aucun intervieweur n'a jamais révélé la partie - - bon à savoir!



4
votes

Si vous avez perl:

parallel eval "{1}" -- "{2}" ::: "traceroute -a -w2" "traceroute6 -w2" ::: google.com debian.org

Comme pour une meilleure façon de joindre la liste des commandes (par exemple, un nom de commande avec un seul paramètre) avec la liste des arguments (par exemple, hostnames), la réponse pourrait être GNU Parallel, qui est conçu pour faire exactement cela:

parallel "{1}" -w2 -- "{2}" ::: traceroute traceroute6 ::: google.com debian.org

Si vous voulez des arguments spéciaux liés à chaque commande, vous pouvez faire:

XXX

Le eval est nécessaire car GNU Parallel cite toutes les entrées, et bien que vous le vouliez normalement, nous ne le voulons pas dans ce cas.

Mais comme il s'agit d'un outil GNU, il est hors de portée de votre question. Il n'est inclus ici que pour les autres personnes qui lisent votre question et qui n'ont pas cette limitation.


10 commentaires

Merci pour l'outil! Je l'ai utilisé dans un projet antérieur il y a quelques années. :-) Existe-t-il une alternative à {1} ? Il est étendu à 1 dans tcsh, et \ {1} est un peu moche. De plus, il semble que j'obtienne une sortie dans le désordre, par exemple, je reçois des bannières initiales de traceroute6 en bas de la sortie (par exemple, l'exécution de votre exemple se termine par la ligne suivante: < code> traceroute6 vers google.com (2607: f8b0: 4005: 809 :: 200e) de 2607: fb90:… , qui semble déplacé et en panne).


Vous pouvez vous protéger contre l'interprétation de tcsh en citant. "{1}" ou "{1}" ou en citant la chaîne entière. Le désordre est probablement traceroute qui imprime à la fois vers stderr et stdout. GNU Parallel imprime d'abord stdout suivi de stderr. Vous pouvez éviter cela en redirigeant stderr vers stdout dans votre commande.


Alors, comment faites-vous la fusion / redirection stderr / stdout dans le cadre d'un parallèle? Qu'en est-il de l'énoncé du problème d'origine - puis-je avoir une liste de commandes avec un argument chacune? Par exemple, traceroute a peut-être besoin de -aw2 , mais traceroute6 ne prend pas en charge -a donc, obtient juste -w2 ? Y-a-t-il un moyen de faire ça?


@cnst Je ne sais pas ce que vous demandez. Listez les commandes que vous souhaitez exécuter, puis laissez-nous voir.


J'ai le problème que stdout / stderr ne sont pas imprimés dans l'ordre par parallel , par rapport à ce que traceroute6 génère si vous l'exécutez en dehors de parallel . Comment rediriger le stderr de traceroute6 vers stdout comme vous l'avez mentionné précédemment, en parallèle?


OK, semble que les travaux suivants: parallèle eval \ {1} -w2 - \ {2} '2> & 1' ::: "traceroute -a" traceroute6 ::: ordns.he.net one.one .one.one . Un peu poilu, mais je suppose que ça marche, et c'est assez court? :-)


Il semble que je reçois la sortie dans le désordre (par exemple, un ordre aléatoire pour chaque commande), et il semblerait également que certaines instances de traceroute doivent être tuées, car elles ne parviennent pas à exécuter jusqu'à la fin complète, en affichant uniquement, comme, la première ligne avec le premier hôte. Une idée de comment résoudre ce problème?


J'ai besoin de plus d'informations. Peut-être publier une nouvelle question avec un MCVE?


Eh bien, l'ordre est toujours aléatoire, mais les instances de traceroute tuées semblent varier. Existe-t-il un moyen d'obtenir la sortie dans le même ordre dans lequel les arguments sont fournis?


Utilisez --keep-order



0
votes

Avec GNU Parallel , la solution finale au problème en jeu serait quelque chose comme l'extrait de code suivant , en utilisant la syntaxe tcsh et OS X traceroute et traceroute6 :

(historique 1; parallèle --keep-order -j4 eval \ {1} -w1 - \ {2} '2> & 1' ::: "traceroute -a -f1" traceroute6 ::: ordns.he.net ns {1,2,3,4,5} .he.net one.one.one.one google-public-dns-a.google.com resolver1.opendns.com; historique 1) | & mail -s "traceroute: ordns.he.net et al de X" receiver@example.org -f sender@example.org


0 commentaires