Je veux faire ça:
Juste à titre d'exemple, disons que je veux obtenir le nom de la commande d'un $PID
_ (veuillez noter qu'il ne s'agit que d'un exemple. Je ne suggère pas qu'il s'agisse du moyen le plus simple d'obtenir le nom d'une commande à partir d'un identifiant de processus. Mon problème est une autre commande dont le format de sortie est incontrôlable).
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 oui ps | egrep 11383
et obtenir
11383 pts/1 00:00:00 bash
L'étape suivante: ps | egrep 11383 | cut -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 l’apparence d’une table, cut
sélectionne un chaîne vide. Bien sûr, je pourrais utiliser cut
pour sélectionner le 7ème champ et non le 4ème champ, mais comment puis-je le savoir, en particulier lorsque la sortie est variable et inconnue au préalable.
Un moyen simple consiste à ajouter une passe de tr
pour faire sortir tous les séparateurs de champs répétés:
$ ps | egrep 11383 | tr -s ' ' | cut -d ' ' -f 4
Je pense que le moyen le plus simple est d'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 en tête. 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
Si vous coupez, une ligne vide apparaît 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
Cependant, en général, il est encore possible d'utiliser fonctions de 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. S'il y en a plus, , tout le reste sera placé dans b
. En conséquence, 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 ensuite 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'`
Nous nous retrouvons donc avec 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 certaines 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 ;-).
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 de Perl:
ps | egrep 11383 | Perl -lane 'print $F[3]'
-a
active le mode autosplit, qui renseigne le @F
tableau avec les données de la colonne.
Utilisation -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
Utiliser des variables de tableau
set $(ps | egrep "^11383 "); echo $4
ou
A=( $(ps | egrep "^11383 ") ) ; echo ${A[3]}
Obtenir la bonne ligne (exemple pour la ligne n ° 6) est fait avec la tête et la queue et le mot correct (Word n ° 4) peut être capturé avec awk:
command|head -n 6|tail -n 1|awk '{print $4}'
Votre commande
ps | egrep 11383 | cut -d" " -f 4
manque un tr -s
pour presser 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 dans 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
si elle 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:"
Au lieu de faire tous ces greps et d'autres 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.