web-dev-qa-db-fra.com

Passer la sortie de la commande précédente à la suivante comme argument

J'ai une commande qui génère des données vers stdout (command1 -p=aaa -v=bbb -i=4). La ligne de sortie peut avoir la valeur suivante:

rate (10%) - name: value - 10Kbps

Je veux grep cette sortie afin de stocker ce "taux" (je suppose que le tuyau sera utile ici). Et enfin, je voudrais que ce taux soit une valeur d'un paramètre sur une deuxième commande (disons command2 -t=${rate})

Cela semble difficile de mon côté; J'aimerais mieux savoir comment utiliser les pipes, grep, sed et ainsi de suite.

J'ai essayé beaucoup de combinaisons comme celle-ci, mais je suis confus:

$ command1 -p=aaa -v=bbb -i=4 | grep "rate" 2>&1 command2 -t="rate was "${rate}
140
Paul

Vous confondez deux types d'entrées très différents.

  1. Entrée standard (stdin)
  2. Arguments de ligne de commande

Ils sont différents et utiles à des fins différentes. Certaines commandes peuvent accepter des entrées dans les deux sens, mais elles les utilisent généralement différemment. Prenons par exemple la commande wc:

  1. Passer l'entrée par stdin:

    ls | wc -l
    

    Cela comptera les lignes dans la sortie de ls

  2. Passer l'entrée par des arguments de ligne de commande:

    wc -l $(ls)
    

    Cela comptera les lignes dans la liste des fichiers imprimé par ls

Des choses complètement différentes.

Pour répondre à votre question, il semble que vous souhaitiez capturer le débit à partir de la sortie de la première commande, puis utiliser le débit comme argument de ligne de commande pour la deuxième commande. Voici une façon de procéder:

rate=$(command1 | sed -ne 's/^rate..\([0-9]*\)%.*/\1/p')
command2 -t "rate was $rate"

Explication du sed:

  • La commande s/pattern/replacement/ Consiste à remplacer un modèle
  • Le motif signifie: la ligne doit commencer par "rate" (^rate) Suivi de deux caractères quelconques (..), Suivis de 0 ou plusieurs chiffres, suivis d'un % , suivi du reste du texte (.*)
  • \1 Dans le remplacement signifie le contenu de la première expression capturée dans \(...\), dans ce cas, les chiffres avant le signe %
  • Le drapeau -n De la commande sed signifie de ne pas imprimer les lignes par défaut. Le p à la fin de la commande s/// Signifie imprimer la ligne en cas de remplacement. En bref, la commande n'imprimera quelque chose qu'en cas de correspondance.
159
janos

J'ai tendance à utiliser ceci:

command1 | xargs -I{} command2 {}

Passer la sortie de command1 via xargs en utilisant la substitution (les accolades) à command2. Si command1 est find assurez-vous d'utiliser -print0 et ajouter -0 à xargs pour les chaînes terminées par null et xargs appellera command2 pour chaque chose trouvée.

Dans votre cas (et en prenant la ligne sed de @Janos):

command1 -p=aaa -v=bbb -i=4 | sed -ne 's/^rate..\([0-9]*\)%.*/\1/p' | xargs -I{} command2 -t="rate was {}"
26
Michael Anderson

Pour simuler la sortie de command1 J'utilise cette instruction echo:

$ echo -e "Foo\nrate (10%) - name: value - 10Kbps\nBar"
$ alias command1='echo -e "Blah\nrate (10%) - name: value - 10Kbps\nBlag"'

Un test rapide:

$ command1
Blah
rate (10%) - name: value - 10Kbps
Blag

C'est tout bon, alors analysons-le:

$ command1 | grep 'rate'
rate (10%) - name: value - 10Kbps

Nous obtenons donc la ligne que nous voulons hors de command1, Passons cela dans command2:

$ alias command2='echo'
$ command2 -t="rate was "$(command1 | grep 'rate')
-t=rate was rate (10%) - name: value - 10Kbps

Je m'attends à ce que "rate was "$(command1 | grep 'rate') concatène automatiquement. Si cela ne fonctionne pas à cause des espaces, vous devriez pouvoir passer l'entrée comme ceci à la place:

$ alias command2='echo'
$ command2 -t=$(echo '"rate was ' $(command1 | grep 'rate') '"')
-t="rate was rate (10%) - name: value - 10Kbps "
5
user56452

Essayez ceci en utilisant grep & pcre :

command | grep -oP '\$>\s+rate\s+\(\K[^\)]+'
4
Gilles Quenot

La première tâche consiste à extraire le taux de cette ligne. Avec GNU grep (Linux non intégré ou Cygwin), vous pouvez utiliser l'option -o. La partie que vous voulez est celle qui ne contient que des chiffres et suivie d'un %. Si vous ne voulez pas extraire le % Lui-même, vous avez besoin d'une astuce supplémentaire: a assertion d'anticipation de largeur nulle , qui ne correspond à rien mais seulement si cela rien n'est suivi de %.

command1 -p=aaa -v=bbb -i=4 | grep -o -P '[0-9]+(?=%)'

Une autre possibilité est d'utiliser sed. Pour extraire une partie d'une ligne dans sed, utilisez la commande s, avec une expression régulière qui correspond à la ligne entière (commençant par ^ Et se terminant par $), Avec le partie à conserver dans un groupe (\(…\)). Remplacez la ligne entière par le contenu du ou des groupes à conserver. En général, passez l'option -n Pour désactiver l'impression par défaut et placez le modificateur p pour imprimer les lignes où il y a quelque chose à extraire (ici, il n'y a qu'une seule ligne donc ça n'a pas d'importance). Voir Renvoyer uniquement la partie d'une ligne après un motif correspondant et Extraire une expression régulière correspondant à 'sed' sans imprimer les caractères environnants pour plus d'astuces sed.

command1 -p=aaa -v=bbb -i=4 | sed 's/^.*rate(\([0-9]*\)%).*$/\1/'

Encore plus flexible que sed, c'est génial. Awk exécute les instructions pour chaque ligne dans un petit langage impératif. Il existe de nombreuses façons d'extraire le taux ici; Je sélectionne les deuxièmes champs (les champs sont délimités par des espaces par défaut) et j'y supprime tous les caractères qui ne sont pas des chiffres.

command1 -p=aaa -v=bbb -i=4 | awk '{gsub(/[^0-9]+/, "", $2); print $2}'

L'étape suivante, maintenant que vous avez extrait le taux, consiste à le passer en argument à command2. L'outil pour cela est un commande susbtitution . Si vous placez une commande dans $(…) (parenthèse dollar), sa sortie est substituée dans la ligne de commande. La sortie de la commande est divisée en mots séparés à chaque bloc d'espaces, et chaque mot est traité comme un motif générique; à moins que vous ne vouliez que cela se produise, mettez des guillemets doubles autour de la substitution de commande: "$(…)". Avec les guillemets doubles, la sortie de la commande est utilisée directement en tant que paramètre unique (la seule transformation est que les retours à la ligne à la fin de la sortie sont supprimés).

command2 -t "$(command1 -p=aaa -v=bbb -i=4 |
               sed 's/^.*rate(\([0-9]*\)%).*$/\1/')"

J'utilise généralement `command` pour placer sa sortie comme argument d'une autre commande. Par exemple, pour trouver des ressources consommées par le processus foo sur freebsd, il faudra:

procstat -r `pgrep -x foo`

Ici, pgrep est utilisé pour extraire le PID du processus foo qui est passé à la commande procstat qui attend le PID du processus comme argument.

2
Vishal Sahu

Vous pouvez utiliser grep et c'est PCRE - Perl Compatible Regular Expressions. Cela vous permet d'utiliser un lookbehind pour faire correspondre la valeur du taux, sans inclure la chaîne "taux" dans vos résultats lors de la recherche.

Exemple

$ echo "rate (10%) - name: value - 10Kbps" | grep -oP '(?<=^rate \()\d+'
10

Détails

Le grep ci-dessus fonctionne comme suit:

  • -o Renverra uniquement ce que nous recherchons, \d+, Qui sont les chiffres entre les parenthèses.
  • -P Active la fonction PCRE de grep
  • (?<=^rate \() Ne renverra que les chaînes commençant par "rate ("

command2

Pour saisir la valeur de "rate", vous pouvez exécuter command1 Comme ceci:

$ rate=$(command 1 | grep -oP '(?<=^rate \()\d+'

Ensuite, pour votre 2e commande, vous utiliseriez simplement cette variable.

$ command2 -t=${rate}

Vous pourriez devenir chic et faire tout cela sur une seule ligne:

$ command2 -t=$(command1 | grep -oP '(?<=^rate \()\d+')

Cela exécutera command1 à l'intérieur du bloc d'exécution $(..), prendra ses résultats et les inclura dans le commutateur -t=.. De command2.

1
slm