[LINUX] sudo rm -rf / * Recréez la commande ps avec l'épave

1.Tout d'abord

Dans l'article précédent (https://qiita.com/minnsou/items/60a306510f613a666fec), après sudo rm -rf / *, implémentez selective sorting pour obtenir la chaîne numérique saisie par l'utilisateur. J'ai fait quelque chose comme trier et écrire dans un fichier.

Je ne pouvais pas utiliser la commande ps, donc j'ai à peine abordé les questions liées au processus. Donc, cette fois, je voudrais implémenter la commande ps disparue uniquement à partir de la commande intégrée __bash __. Correctement cette fois aussi sudo rm -rf / * depuis le début. スクリーンショット 2020-03-23 17.57.34.png Fondamentalement, je présenterai les commandes et la syntaxe qui apparaissent, mais je ne traiterai pas de celles présentées dans l'article précédent telles que les définitions de fonctions et de variables, les tableaux, les instructions if et les instructions for en détail. Veuillez vous référer à Article précédent. Allons-y.

· Les références O'Reilly Japan Introduction bash

・ Environnement d'exécution Raspberry Pi 4 Model B+ Raspbian Buster Lite 2020-02-13-raspbian-buster-lite.img

2 Préparation

Il y a beaucoup de commandes qui ont disparu avec sudo rm -rf / *, mais ls et cat sont souvent utilisés et devraient être redéfinis. De plus, le complément est étrange, alors je vais le réparer.

2.1 Commande ls

Vous pouvez utiliser ʻecho * comme alias pour ls(c'est-à-dire ʻalias ls = "echo *"), mais comme c'est un gros problème, la commande ls qui affiche le répertoire en bleu et les autres en blanc faire.

Pour ce faire, utilisez la commande test (idem pour la commande [). Cette commande utilise l'opérateur d'attribut __file __ pour déterminer la condition. Par exemple, l'opérateur d'attribut de fichier «-a» indique «si le fichier existe». Regardons un exemple concret. スクリーンショット 2020-03-23 18.28.46.png Dans /, le répertoire proc existe et renvoie 0 comme statut de sortie, mais le répertoire bin n'existe plus et renvoie 1.

Utilisez ceci pour créer la commande ls. -d est un opérateur d'attribut de fichier qui indique si un répertoire existe.

.bash


function ls 
{ 
for i in *
do
  if [ -d $i ]
  then 
    echo -ne "\e[34m$i\e[m\t" #Le répertoire est bleu
  else
    echo -ne "$i\t" #A part ça, c'est blanc
  fi
done
echo #Écho pour les sauts de ligne
}

J'ai renvoyé à l'article ici pour colorier avec ʻecho. De plus, le caractère de tabulation (\ t) est inséré entre les noms de fichier. Utilisons réellement ce lsdans un répertoire approprié. <img width="718" alt="スクリーンショット 2020-03-23 20.56.34.png " src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395943/2bf63ea8-0be9-56a1-ce0a-a9fc99e7cafb.png "> Les sauts de ligne sont un peu étranges contrairement aulsnormal, mais faisons-le cette fois. Je pense que vous pouvez obtenir un résultat un peu plus beau si vous utilisez la variable shellCOLUMNS` qui représente le nombre de colonnes d'affichage du terminal pour séparer les cas.

2.2 Commande de chat

Utilisez l'instruction while pour cela. La syntaxe est:

while <Expression conditionnelle>
do
  <Une série de phrases>
done

Pour «», l'état de fin de la commande est utilisé comme dans l'instruction if. Par exemple:

.bash


a=0
while [ $a -ne 3 ]
do
  echo $a
  let a++
done

«-ne» signifie pas égal. Lorsque la variable shell ʻaest différente de 3,[$ a -ne 3] retourne un état de terminaison de 0 et boucle, et quand ʻa devient 3, il retourne 1 et quitte la boucle. La commande intégrée let évalue un opérateur arithmétique et affecte le résultat à une variable. Dans l'exemple ci-dessus, nous incrémentons simplement «a».

Lorsque vous l'essayez, ce sera comme suit. スクリーンショット 2020-03-23 23.32.17.png Lorsque ʻa` devient 3, vous pouvez voir que la commande s'est terminée correctement en quittant la boucle.

Maintenant, implémentons la commande cat en utilisant l'instruction while. Pour plus de simplicité, nous n'implémenterons que la possibilité de prendre un argument et d'afficher son contenu.

.bash


function cat 
{
while read val
do
  echo $val
done
} < $1

La commande read est une commande qui affecte une valeur à une variable shell. Dans cet exemple, la variable shell «val» reçoit le contenu du fichier ligne par ligne et s'affiche avec «écho». Lorsque le contenu du fichier spécifié par $ 1 est vide, la commande read renvoie 1 comme état de fin, afin que vous puissiez sortir correctement de la boucle.

2.3 Achèvement

De plus, sudo rm -rf / * peut ne pas être en mesure de bien se terminer avec tab. スクリーンショット 2020-03-23 18.01.52.png ↓ entrée de l'onglet スクリーンショット 2020-03-23 18.01.44.png J'obtiens une erreur que je n'ai jamais vue. Vous pouvez voir ce qu'est l'achèvement de l'onglet dans la commande cat en utilisant la commande intégrée complete. スクリーンショット 2020-03-24 13.30.56.png Cela signifie que l'achèvement de la commande cat est déterminé par la fonction _longopt. Vous pouvez voir les détails de cette fonction dans declare -f _longopt, mais je ne vais pas y entrer ici. Le fait est que cette fonction __ ne fonctionne pas correctement à cause de l'effet de sudo rm -rf / *.

Cette fois, faisons simplement __complement par le nom de fichier __. Plus précisément, écrivez comme suit en utilisant l'option -f qui se termine à partir d'un nom de fichier normal.

complete -f cat

Comme mentionné dans Article précédent, la complétion du nom de fichier peut être faite avec ʻESC + / , mais maintenant cela peut aussi être fait avec tab. Au fait, rendons tab également disponible pour la commande cd. Avec l'option -d`, la complétion se fait à partir de __directory name __.

complete -d cd

3 Création d'une commande ps

Créons maintenant la commande ps. Commencez par une description du répertoire / proc qui contient les informations de processus.

3.1 Répertoire / proc

Sous Linux, il existe un répertoire / proc qui contient des informations sur les processus. Ceci est un pseudo répertoire différent du répertoire normal, et il ne disparaît pas même si sudo rm -rf / *.

Quand vous le regardez, cela ressemble à ceci. スクリーンショット 2020-03-23 20.57.53.png Le répertoire composé uniquement des chiffres supérieurs contient les informations de processus correspondant à l'ID de processus (PID).

Au fait, ce répertoire / proc existe sous Linux, mais sous UNIX pur (par exemple, FreeBSD et Mac OS BSD, Solaris basé sur System V, etc.) __ n'existe pas __, ou même s'il existe __ Veuillez noter que le contenu peut être différent __.

Jetons maintenant un coup d'œil au répertoire de l'ID de processus de ce shell de connexion (c'est-à-dire bash). Vous pouvez trouver l'ID de processus du shell actuel avec ʻecho $$ `. スクリーンショット 2020-03-23 21.00.24.png Il contient un fichier appelé «stat», qui contient des informations sur le processus. スクリーンショット 2020-03-23 21.01.02.png Commençons depuis le début.

Je ne peux pas tous les couvrir, alors voyez man proc pour plus de détails.

La commande ps capture ces valeurs et les affiche à l'écran. Cependant, la commande ps par défaut est trop peu de processus à afficher, donc je voudrais créer une commande ps a avec l'option ʻa, c'est-à-dire une commande ps` qui affiche tous les processus qui ont des terminaux.

Tout d'abord, créez une fonction read_stat qui prend ce fichier stat comme argument et l'assigne à une variable shell.

.bash


function read_stat
{
read -a values
pid=${values[0]}
comm=${values[1]}
state=${values[2]}
tty_nr=${values[6]}
time=$(( ${values[13]} / 100 ))
} < $1

Tout d'abord, utilisez l'option -a de la commande read pour placer chaque valeur de stat dans le tableau appelé values. Vous trouverez ci-dessous une description des variables shell.

Je vais vraiment l'exécuter. スクリーンショット 2020-03-23 21.29.16.png Vous pouvez voir que la valeur est entrée correctement.

Je peux afficher les informations de base avec cela, mais le nom du terminal ne peut pas être affiché correctement. C'est parce que vous devez convertir le nombre de tty_nr (34816 dans l'exemple ci-dessus) en une chaîne comme tty1 ou pts / 0. Parlons un peu des fichiers device ici.

3.2 Fichier de l'appareil

Dans les systèmes d'exploitation UNIX, le matériel tel que le disque dur, la mémoire USB et le terminal peuvent également être traités comme des fichiers. Un tel fichier est appelé device file. / Dev / null pour ignorer la sortie et / dev / random pour générer une chaîne aléatoire sont également des fichiers de périphérique que vous avez peut-être utilisés. Il existe deux types de fichiers de périphérique: block type et character type. Le premier est un fichier pour faire fonctionner les périphériques __disk, et le second est un fichier pour gérer d'autres choses.

Les fichiers de périphérique sont gérés à l'aide du __major number __ et du __minor number __. Vérifions la documentation Linux actuelle.

https://github.com/torvalds/linux/blob/master/Documentation/admin-guide/devices.txt

スクリーンショット 2020-03-23 22.59.37.png

Par exemple, "Numéro majeur 1 du fichier de périphérique bloc" est affecté au "disque RAM". Le nombre mineur indique le numéro du disque RAM.

Le terminal affiché par la commande ps est essentiellement TTY device avec le numéro majeur 4 ou Unix98 PTY slave avec le numéro majeur 136. Le périphérique TTY est l'écran qui est directement connecté à Raspai, et l'esclave Unix98 PTY est un type de pseudo terminal, qui est l'écran lorsqu'il est connecté par __SSH.

Revenons maintenant à tty_nr. La signification de cette chaîne numérique est écrite dans man proc, je vais donc la citer.

(7) tty_nr %d The controlling terminal of the process. (The minor device number is contained in the combination of bits 31 to 20 and 7 to 0; the major device number is in bits 15 to 8.)

Les 31e à 20e et 7e à 0e bits de la chaîne numérique tty_nr sont des nombres mineurs, et les 15e à 8e bits sont des nombres majeurs.

Utilisez-les pour créer une fonction qui affiche le nom du terminal dans la variable shell tty.

.bash


function get_tty
{
major_num=$((tty_nr>>8))
minor_num=$((tty_nr&255))
if [ $major_num -eq 4 ]
then
  tty=tty${minor_num}
elif [ $major_num -eq 136 ]
then
  tty=pts/${minor_num}
else
  tty=???
fi
}

Le nombre majeur est extrait en utilisant le décalage gauche de 8 bits (tty_nr >> 8), et le nombre mineur est extrait du produit logique ( tty_nr & 255) avec 255 qui est 11111111 en binaire. En fait, ce n'est pas précis à moins d'utiliser 31 à 20 bits, mais cette fois, il n'y a pas de problème, donc je l'ai fait de cette façon.

Si le nombre majeur est 4, c'est un terminal normal (tty1, etc.), s'il est 136, c'est un pseudo-terminal ( pts / 0, etc.), sinon il est inconnu (???), et celui qui lui correspond est la variable shell tty. Attribué à .

Utilisons-le réellement. スクリーンショット 2020-03-23 23.13.15.png Puisque cette expérience a été réalisée en se connectant depuis mac avec SSH, le pseudo terminal s'est affiché correctement.

3.3 Créer une commande ps

Créez une commande ps en utilisant les fonctions read_stat et get_tty créées jusqu'à présent.

.bash


function ps { 
echo -e "PID\tTTY\tSTATE\tTIME\tCMD"
for stat in /proc/[1-9]*/stat #Considérez un répertoire qui commence par un nombre et a une statistique
do 
  read_stat $stat
  if [ $tty_nr -ne 0 ] #Afficher uniquement les processus avec des terminaux
  then
    get_tty #Obtenez tty
    echo -e "${pid}\t${tty}\t${state}\t${time}\t${comm:1:-1}" #De la valeur de la communication()Retirer
  fi
done
}

Ici, la variable shell comm a une chaîne entre parenthèses, donc je l'ai supprimée et l'ai sortie ($ {comm: 1: -1}).

Utilisons-le réellement. スクリーンショット 2020-03-23 23.11.26.png Il a été produit correctement. Cette fois, j'ai également constaté que bash s'exécute sur le moniteur qui est directement connecté à la tarte aux râpes et que son PID est 614.

À la fin

Je pense que vous pouvez réaliser que vous pouvez faire n'importe quoi de manière inattendue avec uniquement les commandes intégrées de bash. Si j'ai le temps, j'aimerais parler de la gestion des processus (concepts de travail, commandes kill, signaux, etc.).

Recommended Posts

sudo rm -rf / * Recréez la commande ps avec l'épave
commande ps "wchan"