Je cherche un moyen de répertorier toutes les sessions, quelque chose comme la commande who, qui indiquerait également le type de shell utilisé par la liste des utilisateurs.
Selon ce que vous voulez, vous pouvez utiliser le cousin de who
de w
:
$ w
17:40:49 up 11 days, 22:38, 4 users, load average: 0.14, 0.13, 0.10
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
muru tty7 :0 12Jan17 11days 2:24m 0.37s /sbin/upstart --user
muru pts/24 127.0.0.1:S.0 17:36 0.00s 0.41s 0.00s w
muru pts/26 127.0.0.1:S.2 17:38 2:33 0.19s 0.19s -/bin/zsh
Il exécute la commande active sur le terminal de connexion. Vous pouvez utiliser l'ID de terminal pour voir quel shell j'ai démarré à l'origine:
$ w -h | awk '{print $2}' | xargs -L1 pgrep -oat
1969 /usr/lib/xorg/Xorg -core :0 -seat seat0 -auth /var/run/lightdm/root/:0 -nolisten tcp vt7 -novtswitch
12703 -/bin/zsh
13398 -/bin/zsh
pgrep
peut:
-t
)-o
)-a
)Comme tous les shells installés sur votre système sont répertoriés dans /etc/shells
, vous pouvez commencer à partir de là et utiliser ps
pour répertorier ceux qui sont en cours d'exécution et les informations associées.
Alors, d'abord, obtenez la liste des coquillages:
$ grep -oP '^/.*/\K.*' /etc/shells | sort -u
bash
csh
dash
fish
ksh
mksh
sh
tcsh
zsh
-o
signifie "n'imprime que la partie correspondante de la ligne" et -P
active PCRE, expressions régulières compatibles Perl, qui nous permettent d'utiliser des éléments fantaisistes tels que \K
, qui signifie "tout ignorer assorties à ce point ". Donc, ^/.*/\K.*
signifie faire correspondre les lignes commençant par un /
(^/
), puis autant de caractères que possible jusqu'à un /
(.*/
), puis oublier ce qui était assorti jusqu'à présent (\K
) et correspond à tout le reste jusqu'à la fin de la ligne. Cela renverra effectivement la dernière partie de chaque ligne, le nom réel du shell (par exemple, bash
pour /bin/bash
).
Le sort -u
s'assure simplement qu'aucun shell n'est imprimé deux fois (vous pouvez parfois avoir le même Shell dans les deux /bin
et /usr/bin
).
Mais nous souhaitons utiliser cette liste pour rechercher la sortie de ps
, nous en avons donc besoin dans un format que grep
peut comprendre:
$ grep -oP '^/.*/\K.*' /etc/shells | sort -u | Perl -pe '!eof && s/\n/\|/'
bash|csh|dash|fish|ksh|mksh|sh|tcsh|zsh
Cette commande Perl
remplace simplement les caractères de nouvelle ligne (\n
) par |
, afin que cela puisse être directement transmis à grep
.
Nous utilisons maintenant pgrep
pour répertorier tous les processus correspondants:
$ pgrep -x "$(grep -oP '^/.*/\K.*' /etc/shells | sort -u |
Perl -pe '!eof && s/\n/\|/')"
1235
5399
14031
14116
14200
14304
14337
14392
15257
15368
15551
15601
15704
15877
17033
28177
29138
30797
32404
32656
Le -x
est nécessaire pour que seuls les processus qui correspondent exactement (donc pas de foosh
pour sh
).
Maintenant que nous avons la liste des PID cibles, nous pouvons les transmettre à ps
et les utiliser pour imprimer les informations pertinentes:
$ pgrep -x "$(grep -oP '^/.*/\K.*' /etc/shells | sort -u |
Perl -pe '!eof && s/\n/\|/')" |
while read pid; do ps -p $pid -o pid=,cmd=,euser=,tty=; done
1235 /bin/bash terdon pts/1
5399 /bin/bash terdon pts/4
12341 /bin/bash terdon pts/2
14031 /bin/bash terdon pts/8
14116 /bin/bash terdon pts/9
14200 /bin/bash terdon pts/10
14304 /bin/bash terdon pts/11
14337 /bin/bash terdon pts/12
14392 /bin/bash terdon pts/13
15257 dash terdon pts/13
15368 zsh terdon pts/12
15551 mksh terdon pts/11
15601 -sh terdon pts/10
15704 -csh terdon pts/9
15877 sh terdon pts/8
17033 /bin/bash terdon pts/2
28177 /bin/bash terdon pts/3
29138 fish terdon pts/3
30797 /bin/bash terdon pts/5
32404 /bin/bash terdon pts/6
32656 /bin/bash terdon pts/7
Ceci lit chaque PID produit par les étapes précédentes et exécute ps
pour lui (-p $pid
), en utilisant le drapeau -o
pour contrôler la sortie pour imprimer le PID, la commande utilisée, l'utilisateur qui l'a lancé. lui et le terminal auquel il est attaché.
Informations shell avec processus enfants
ps -efH | egrep 'pts/|tty'
sup 14536 2065 0 17:05 pts/19 00:00:00 bash
sup 14572 14536 0 17:05 pts/19 00:00:00 dash
sup 14575 14572 0 17:05 pts/19 00:00:00 bash
sup 14611 14575 0 17:05 pts/19 00:00:00 dash
sup 14673 1956 0 17:06 pts/6 00:00:00 /bin/bash
sup 14717 14673 0 17:06 pts/6 00:00:00 bash
sup 15650 14717 0 17:16 pts/6 00:00:00 ps -efH
Voici un script qui répertorie le PID d'un shell, son binaire, le terminal auquel le stdin du shell est attaché et le nom d'utilisateur du propriétaire du processus. Le script utilise largement le système de fichiers /proc
pour une meilleure précision.
Ce que vous voyez dans la capture d'écran ci-dessous est un exemple d'exécution de ce script. J'ai deux divisions ouvertes avec l'émulateur de terminal Terminator. Le nombre entre crochets de chaque invite correspond au PID de ce shell. La division du haut confirme que deux instances mksh
sont ouvertes dans des pseudo-terminaux virtuels, à savoir des terminaux à interface graphique, et une instance bash. Une autre instance de mksh
peut être trouvée dans tty1
. Ce qui est également intéressant, c’est qu’il existe deux instances dash
attachées à /dev/null
. En examinant les processus auxquels ils appartiennent, il s’avère que ceux-ci appartiennent à un seul service Unity Indicator et Zeitgeist. Ainsi, dans ce script, il est plus facile de voir quels shells sont réellement utilisés par de vrais utilisateurs, et lesquels par les processus système.
La source du script est ci-dessous et également disponible sur GitHub
#!/usr/bin/env bash
is_self(){
if [ "$link" == "/bin/bash" ] && grep -q $0 /proc/$proc_pid/cmdline
then
return 0
else
return 1
fi
}
print_proc_info(){
terminal=$( readlink -e "/proc/$proc_pid/fd/0" )
[ -z "$terminal" ] && terminal=$'\t'
printf "%s\t%s\t%s\t" "$proc_pid" "$1" "$terminal"
stat --printf="%U\n" /proc/"$proc_pid"/mountstats
}
find_process(){
local function_pid=$$
local search_base=$(basename "$1")
find /proc -maxdepth 1 -type d -path "*/[1-9]*" | while read -r proc_dir;
do
local proc_pid=$(basename "$proc_dir")
local link=$(readlink -e "$proc_dir"/exe)
local name=$( awk 'NR==1{print $2}' "$proc_dir"/status 2>/dev/null )
if is_self ; then continue ; fi
if [ "$link" == "$1" ] ||
[ -z "$link" ] && [ "$name" = "$search_base" ]
then
print_proc_info $1
# make additional check if readlink wasn't allowed to
# get where /proc/[pid]/exe is symlinked
fi
done
}
main(){
while read -r Shell
do
find_process "$Shell"
done < /etc/shells
echo "Done, press [ENTER] to continue"
read
}
main