Question

Je veux faire ceci:

  1. lancer une commande
  2. capturer la sortie
  3. sélectionnez une ligne
  4. sélectionnez une colonne de cette ligne

Juste à titre d'exemple, supposons que je souhaite obtenir le nom de la commande à partir d'un $ PID (veuillez noter qu'il ne s'agit que d'un exemple, je ne suggère pas qu'il s'agit du moyen le plus simple d'obtenir un nom de la commande d'un identifiant de processus - mon vrai problème est avec une autre commande dont je ne peux pas contrôler le format de sortie).

Si je lance ps , je reçois:


  PID TTY          TIME CMD
11383 pts/1    00:00:00 bash
11771 pts/1    00:00:00 ps

Maintenant, je fais ps | egrep 11383 et obtenez

11383 pts/1    00:00:00 bash

Étape suivante: ps | egrep 11383 | couper-d " " -f 4 . La sortie est:

<absolutely nothing/>

Le problème est que cut coupe la sortie par des espaces simples et que ps ajoute des espaces entre les deuxième et troisième colonnes pour conserver la ressemblance d'un tableau, < code> cut sélectionne une chaîne vide. Bien sûr, je pourrais utiliser cut pour sélectionner le 7ème et non le 4ème champ, mais comment puis-je le savoir, en particulier lorsque la sortie est variable et inconnue au préalable.

Était-ce utile?

La solution

Une solution simple consiste à ajouter un code tr pour extraire les séparateurs de champs répétés:

$ ps | egrep 11383 | tr -s ' ' | cut -d ' ' -f 4

Autres conseils

Je pense que le moyen le plus simple consiste à utiliser awk . Exemple:

$ echo "11383 pts/1    00:00:00 bash" | awk '{ print $4; }'
bash

Veuillez noter que l'option tr -s '' "ne supprimera aucun espace précédant unique. Si votre colonne est alignée à droite (comme avec ps pid) ...

$ ps h -o pid,user -C ssh,sshd | tr -s " "
 1543 root
19645 root
19731 root

Ensuite, la découpe donnera une ligne vide pour certains de ces champs s'il s'agit de la première colonne:

$ <previous command> | cut -d ' ' -f1

19645
19731

Sauf si vous le faites précéder d'un espace, évidemment

$ <command> | sed -e "s/.*/ &/" | tr -s " "

Maintenant, pour ce cas particulier de nombres pid (pas de noms), il existe une fonction appelée pgrep :

$ pgrep ssh


Fonctions du shell

Cependant, en général, il est toujours possible d'utiliser les fonctions du shell de manière concise, car la commande read a un intérêt particulier:

$ <command> | while read a b; do echo $a; done

Le premier paramètre à lire, a , sélectionne la première colonne et s'il y en a plus, tout le reste sera placé dans b . . Par conséquent, vous n’avez jamais besoin de plus de variables que le nombre de votre colonne +1 .

Alors,

while read a b c d; do echo $c; done

affichera alors la 3ème colonne. Comme indiqué dans mon commentaire ...

Une lecture canalisée sera exécutée dans un environnement qui ne transmet pas de variables au script appelant.

out=$(ps whatever | { read a b c d; echo $c; })

arr=($(ps whatever | { read a b c d; echo $c $b; }))
echo ${arr[1]}     # will output 'b'`


La solution multidisque

Nous aboutissons alors à la réponse de @frayser qui consiste à utiliser la variable shell IFS qui utilise par défaut un espace pour scinder la chaîne en un tableau. Cela ne fonctionne que dans Bash cependant. Dash et Ash ne le supportent pas. J'ai eu beaucoup de mal à scinder une chaîne en composants dans une boîte de dialogue Busybox. Il est assez facile d’obtenir un seul composant (par exemple, en utilisant awk), puis de le répéter pour chaque paramètre dont vous avez besoin. Mais vous finissez par appeler awk à plusieurs reprises sur la même ligne ou en utilisant à plusieurs reprises un bloc de lecture avec écho sur la même ligne. Ce qui n'est ni efficace ni joli. Vous finissez donc par scinder en utilisant $ {name %% *} et ainsi de suite. Vous avez envie de compétences en Python, car en fait, les scripts shell ne sont plus très amusants si la moitié ou plus des fonctionnalités auxquelles vous êtes habitué ont disparu. Mais vous pouvez supposer que même Python ne serait pas installé sur un tel système, et ce n’était pas le cas; -).

essayer

ps |&
while read -p first second third fourth etc ; do
   if [[ $first == '11383' ]]
   then
       echo got: $fourth
   fi       
done

Semblable à la solution awk de brianegge, voici l'équivalent Perl:

ps | egrep 11383 | perl -lane 'print $F[3]'

-a active le mode autosplit, qui remplit le tableau @F avec les données de colonne.
Utilisez -F, si vos données sont délimitées par des virgules, plutôt que par des espaces.

Le champ 3 est imprimé car Perl commence à compter à partir de 0 au lieu de 1

Obtenir la bonne ligne (exemple pour la ligne n ° 6) est fait avec tête et queue et le mot correct (mot n ° 4) peut être capturé avec awk:

command|head -n 6|tail -n 1|awk '{print $4}'

Utilisation de variables de tableau

set $(ps | egrep "^11383 "); echo $4

ou

A=( $(ps | egrep "^11383 ") ) ; echo ${A[3]}

Au lieu de faire tous ces greps et ce genre de choses, je vous conseillerais d'utiliser les fonctionnalités ps de changement de format de sortie.

ps -o cmd= -p 12345

Vous obtenez la ligne cmmand d'un processus avec le pid spécifié et rien d'autre.

Ceci est conforme à POSIX et peut donc être considéré comme portable.

Votre commande

ps | egrep 11383 | cut -d" " -f 4

manque un tr -s pour serrer les espaces, comme l'explique le déroulement dans sa réponse .

Cependant, vous voudrez peut-être utiliser awk , car il gère toutes ces actions en une seule commande:

ps | awk '/11383/ {print $4}'

Ceci affiche la 4ème colonne de ces lignes contenant 11383 . Si vous voulez que cela corresponde à 11383 s'il apparaît au début de la ligne, vous pouvez dire ps | awk '/ ^ 11383 / {print $ 4}' .

Le set de Bash analysera toutes les sorties dans les paramètres de position.

Par exemple, avec la commande set $ (free -h) , echo $ 7 affichera "Mem:"

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top