En supposant un grep simple tel que:
$ psa aux | grep someApp
1000 11634 51.2 0.1 32824 9112 pts/1 SN+ 13:24 7:49 someApp
Cela fournit beaucoup d'informations, mais comme la première ligne de la commande ps est manquante, il n'y a pas de contexte pour les informations. Je préférerais que la première ligne de ps soit également affichée:
$ psa aux | someMagic someApp
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
1000 11634 51.2 0.1 32824 9112 pts/1 SN+ 13:24 7:49 someApp
Bien sûr, je pourrais ajouter une expression régulière à grep spécifiquement pour ps:
$ ps aux | grep -E "COMMAND|someApp"
Cependant, je préférerais une solution plus générale car il y a d'autres cas dans lesquels j'aimerais également avoir la première ligne.
Il semble que ce serait un bon cas d'utilisation pour n descripteur de fichier "stdmeta" .
Normalement, vous ne pouvez pas faire cela avec grep mais vous pouvez utiliser d'autres outils. AWK a déjà été mentionné mais vous pouvez également utiliser sed
, comme ceci:
sed -e '1p' -e '/youpattern/!d'
L'utilitaire Sed fonctionne sur chaque ligne individuellement, exécutant les commandes spécifiées sur chacune d'elles. Vous pouvez avoir plusieurs commandes, en spécifiant plusieurs -e
options. Nous pouvons ajouter à chaque commande un paramètre de plage qui spécifie si cette commande doit être appliquée à une ligne spécifique ou non.
"1p" est une première commande. Il utilise la commande p
qui imprime normalement toutes les lignes. Mais nous lui ajoutons une valeur numérique qui spécifie la plage à laquelle il doit être appliqué. Ici, nous utilisons 1
qui signifie première ligne. Si vous souhaitez imprimer plus de lignes, vous pouvez utiliser x,yp
où x
est la première ligne à imprimer, y
est la dernière ligne à imprimer. Par exemple, pour imprimer les 3 premières lignes, vous utiliseriez 1,3p
La commande suivante est d
qui supprime normalement toutes les lignes du tampon. Avant cette commande, nous mettons yourpattern
entre deux /
personnages. C'est l'autre façon (la première était de spécifier les lignes comme nous l'avons fait avec la commande p
) d'adresser les lignes sur lesquelles la commande devrait être exécutée. Cela signifie que la commande ne fonctionnera que pour les lignes qui correspondent à yourpattern
. Sauf que nous utilisons !
caractère avant d
commande qui inverse sa logique. Alors maintenant, il supprimera toutes les lignes qui ne pas correspondent au modèle spécifié.
À la fin, sed imprimera toutes les lignes qui restent dans le tampon. Mais nous avons supprimé les lignes qui ne correspondent pas du tampon afin que seules les lignes correspondantes soient imprimées.
Pour résumer: nous imprimons la 1ère ligne, puis nous supprimons toutes les lignes qui ne correspondent pas à notre modèle d'entrée. Les autres lignes sont imprimées (donc seules les lignes qui correspondent au motif).
Comme mentionné dans les commentaires, il y a un problème avec cette approche. Si le motif spécifié correspond également à la première ligne, il sera imprimé deux fois (une fois par la commande p
et une fois en raison d'une correspondance). Nous pouvons éviter cela de deux manières:
Ajouter 1d
commande après 1p
. Comme je l'ai déjà mentionné, la commande d
supprime les lignes du tampon et nous spécifions sa plage par le numéro 1, ce qui signifie qu'elle ne supprimera que la 1ère ligne. La commande serait donc sed -e '1p' -e '1d' -e '/youpattern/!d'
En utilisant 1b
commande, au lieu de 1p
. C'est un truc. La commande b
nous permet de passer à une autre commande spécifiée par une étiquette (de cette façon, certaines commandes peuvent être omises). Mais si cette étiquette n'est pas spécifiée (comme dans notre exemple), elle saute juste à la fin des commandes, ignorant le reste des commandes de notre ligne. Donc, dans notre cas, la dernière commande d
ne supprimera pas cette ligne du tampon.
ps aux | sed -e '1b' -e '/syslog/!d'
Certaines implémentations sed
peuvent vous faire économiser de la frappe en utilisant un point-virgule pour séparer les commandes au lieu d'utiliser plusieurs -e
options. Donc, si vous ne vous souciez pas d'être portable, la commande serait ps aux | sed '1b;/syslog/!d'
. Cela fonctionne au moins dans GNU sed
et busybox
implémentations.
Voici, cependant, une façon assez folle de le faire avec grep. Ce n'est certainement pas optimal, je le poste juste à des fins d'apprentissage, mais vous pouvez l'utiliser par exemple, si vous n'avez pas d'autre outil dans votre système:
ps aux | grep -n '.*' | grep -e '\(^1:\)\|syslog'
Tout d'abord, nous utilisons -n
option pour ajouter des numéros de ligne avant chaque ligne. Nous voulons numéroter toutes les lignes auxquelles nous correspondons .*
- n'importe quoi, même une ligne vide. Comme suggéré dans les commentaires, nous pouvons également faire correspondre "^", le résultat est le même.
Ensuite, nous utilisons des expressions régulières étendues pour pouvoir utiliser \|
caractère spécial qui fonctionne comme OU. Donc, nous correspondons si la ligne commence par 1:
(première ligne) ou contient notre modèle (dans ce cas, son syslog
).
Maintenant, le problème est que nous obtenons ces numéros de ligne laids dans notre sortie. Si c'est un problème, nous pouvons les supprimer avec cut
, comme ceci:
ps aux | grep -n '.*' | grep -e '\(^1:\)\|syslog' | cut -d ':' -f2-
-d
option spécifie le délimiteur, -f
spécifie les champs (ou colonnes) que nous voulons imprimer. Nous voulons donc couper chaque ligne sur chaque :
caractère et imprime uniquement la 2e et toutes les colonnes suivantes. Cela supprime efficacement la première colonne avec son délimiteur et c'est exactement ce dont nous avons besoin.
Que pensez-vous de l'utilisation de awk
au lieu de grep
?
chopper:~> ps aux | awk 'NR == 1 || /syslogd/'
USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND
root 19 0.0 0.0 2518684 1160 ?? Ss 26Aug12 1:00.22 /usr/sbin/syslogd
mrb 574 0.0 0.0 2432852 696 s006 R+ 8:04am 0:00.00 awk NR == 1 || /syslogd/
NR == 1
: Nombre d'enregistrements == 1; c'est à dire. la première ligne||
: ou:/syslogd/
: Modèle à rechercherIl peut également être utile de consulter pgrep
, bien que ce soit plus pour les scripts que pour les sorties destinées aux utilisateurs. Cela évite cependant que la commande grep
elle-même n'apparaisse dans la sortie.
chopper:~> pgrep -l syslogd
19 syslogd
ps aux | { read line;echo "$line";grep someApp;}
EDIT: après commentaires
ps aux | { head -1;grep someApp;}
Je pensais que head -1
lirait toutes les entrées, mais après l'avoir testé, cela fonctionne aussi.
{ head -1;grep ok;} <<END
this is a test
this line should be ok
not this one
END
la sortie est
this is a test
this line should be ok
Ps prend en charge le filtre interne,
Supposons que vous recherchez un processus bash:
ps -C bash -f
Répertorie tous les processus nommés bash
.
J'ai tendance à envoyer l'en-tête à stderr :
ps | (IFS= read -r HEADER; echo "$HEADER" >&2; cat) | grep ps
Ceci est généralement suffisant pour la lecture humaine. par exemple.:
PID TTY TIME CMD
4738 pts/0 00:00:00 ps
La partie entre crochets pourrait entrer dans son propre script pour une utilisation générale.
Il y a une commodité supplémentaire en ce que la sortie peut être davantage canalisée (vers sort
etc.) et l'en-tête restera en haut.
Vous pouvez également utiliser tee
et head
:
ps aux | tee >(head -n1) | grep syslog
Notez cependant que tant que tee
est incapable d'ignorer les signaux SIGPIPE
(voir par exemple discussion ici ) cette approche a besoin d'une solution de contournement pour être fiable. La solution de contournement est d'ignorer les signaux SIGPIPE, cela peut par exemple être fait comme ceci dans des shells comme des shells:
trap '' PIPE # ignore SIGPIPE
ps aux | tee >(head -n1) 2> /dev/null | grep syslog
trap - PIPE # restore SIGPIPE handling
Notez également que l'ordre de sortie n'est pas garanti .
Peut-être que deux commandes ps
seraient plus faciles.
$ ps aux | head -1 && ps aux | grep someApp
USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND
100 3304 0.0 0.2 2466308 6476 ?? Ss 2Sep12 0:01.75 /usr/bin/someApp
Vous pouvez utiliser pidstat avec:
pidstat -C someApp
or
pidstat -p <PID>
Exemple:
# pidstat -C Java
Linux 3.0.26-0.7-default (hostname) 09/12/12 _x86_64_
13:41:21 PID %usr %system %guest %CPU CPU Command
13:41:21 3671 0.07 0.02 0.00 0.09 1 Java
Plus d'informations: http://linux.die.net/man/1/pidstat
Mettez d'abord ce qui suit dans votre fichier .bashrc ou copiez/collez dans Shell pour les tests.
function psls {
ps aux|head -1 && ps aux|grep "$1"|grep -v grep;
}
Utilisation: psls [motif grep]
$ psls someApp
USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND
root 21 0.0 0.0 2467312 1116 ?? Ss Tue07PM 0:00.17 /sbin/someApp
Assurez-vous de vous procurer votre .bashrc (ou .bash_profile si vous le placez à la place):
source ~/.bashrc
La fonction sera même complétée automatiquement sur la ligne de commande Shell. Comme vous l'avez indiqué dans une autre réponse, vous pouvez diriger la première ligne vers un fichier pour enregistrer un appel à ps.
trier mais garder la ligne d'en-tête en haut
# print the header (the first line of input)
# and then run the specified command on the body (the rest of the input)
# use it in a pipeline, e.g. ps | body grep somepattern
body() {
IFS= read -r header
printf '%s\n' "$header"
"$@"
}
Et utilisez-le comme ça
$ ps aux | body grep someApp
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
1000 11634 51.2 0.1 32824 9112 pts/1 SN+ 13:24 7:49 someApp
Merci surtout à Janis Papanagnou dans comp.unix.Shell, j'utilise la fonction suivante:
function grep1 {
IFS= read -r header && printf "%s\n" "$header"; grep "$@"
}
Cela présente un certain nombre d'avantages:
-i
pour une correspondance insensible à la casse, -E
pour les expressions rationnelles étendues, etc.Exemple d'utilisation:
$ ps -rcA | grep1 databases
PID TTY TIME CMD
$ ps -rcA | grep1 -i databases
PID TTY TIME CMD
62891 ?? 0:00.33 com.Apple.WebKit.Databases
Une autre façon avec gnu ed
:
ed -s '!ps aux' <<< $'2,$v/PATTERN/d\n,p\nq\n'
ou, si le shell prend en charge la substitution de processus:
printf '%s\n' '2,$v/PATTERN/d' ,p q | ed -s <(ps aux)
c'est:
2,$v/PATTERN/d - remove all lines not matching pattern (ignore the header)
,p - print the remaining lines
q - quit
Plus portable, sans gnu
'!'
ou substitution Shell - en utilisant uniquement ed
intégré r
à r
lire la sortie de ps aux
dans le tampon, puis supprimez les lignes non correspondantes dans le 2,$
rangez et imprimez le résultat:
printf '%s\n' 'r !ps aux' '2,$v/PATTERN/d' ,p q | ed -s
Et puisque les commandes sed
dans la réponse acceptée affichent également la ligne qui correspond, avec un sed
qui prend en charge -f-
et un shell qui prend en charge la substitution de processus, je lancerais:
printf '%s\n' '2,${' '/PATTERN/!d' '}' | sed -f - <(ps aux)
qui fait à peu près la même chose que les commandes ed
précédentes.
La manière Perl:
ps aux | Perl -ne 'print if /pattern/ || $.==1'
Bien plus facile à lire que sed
, plus rapide, pas de risque de choisir des lignes indésirables.
Si ce n'est que pour les processus de grepping avec des en-têtes complets, je développerais la suggestion de @ mrb:
$ ps -f -p $(pgrep bash)
UID PID PPID C STIME TTY STAT TIME CMD
nasha 2810 2771 0 2014 pts/6 Ss+ 0:00 bash
...
pgrep bash | xargs ps -fp
obtiendra le même résultat mais sans sous-shell. Si un autre formatage est requis:
$ pgrep bash | xargs ps fo uid,pid,stime,cmd -p
UID PID STIME CMD
0 3599 2014 -bash
1000 3286 2014 /bin/bash
...