wiki/TipsShell.md

902 lines
24 KiB
Markdown
Raw Permalink Normal View History

2023-06-27 00:43:25 +02:00
---
categories: tips shell
title: Tips SHell
...
2016-12-21 19:32:52 +01:00
2023-06-27 00:43:25 +02:00
* Style guide : <https://google.github.io/styleguide/shell.xml>
* Bash Reference Manual : <https://www.gnu.org/software/bash/manual/bash.html>
* man dash : <https://linux.die.net/man/1/dash>
2016-12-21 19:32:52 +01:00
2023-06-27 00:43:25 +02:00
Quelques astuces SHell. Des bouts de commandes, ou simplement les arguments qui vont bien.
2017-01-03 09:15:29 +01:00
2023-06-27 00:43:25 +02:00
## Configuration
2017-01-20 10:33:36 +01:00
2023-06-27 00:43:25 +02:00
### Initialisation du shell
2017-01-20 10:33:36 +01:00
2023-06-27 00:43:25 +02:00
Entre les 3 modes ("login", "interactive non-login" et "non-interactive non-login") il y a de quoi se perdre à propos des fichiers chargés.
Voici un rappel assez complet : <https://github.com/rbenv/rbenv/wiki/Unix-shell-initialization>
2017-01-24 11:50:40 +01:00
2023-06-27 00:43:25 +02:00
### Bash history
2017-01-24 11:50:40 +01:00
2023-06-27 00:43:25 +02:00
Pour avoir un « joli » [history](https://www.gnu.org/software/bash/manual/html_node/Bash-History-Builtins.html) sous Bash :
2017-01-24 11:50:40 +01:00
~~~
2018-09-11 14:01:18 +02:00
export HISTCONTROL=$HISTCONTROL${HISTCONTROL+:}ignoreboth:erasedups
2017-01-24 11:50:40 +01:00
export HISTSIZE=65535
export HISTTIMEFORMAT="%c : "
~~~
2023-06-27 00:43:25 +02:00
> *Note* : si besoin on peut `chattr +a /root/.bash_history` pour compliquer sa modification/suppression
2017-03-14 12:22:58 +01:00
2023-06-27 00:43:25 +02:00
Pour avoir un historique sauvegarde à chaque commande saisie et se synchronise entre plusieurs terminaux :
2018-09-11 14:01:18 +02:00
~~~
shopt -s histappend
PROMPT_COMMAND="history -a;history -n;${PROMPT_COMMAND}"
~~~
2023-06-27 00:43:25 +02:00
### Changer l'éditeur de texte par défaut pour une commande
2017-03-14 12:22:58 +01:00
2023-06-28 11:11:57 +02:00
Normalement les commandes utilisant un éditeur (`vipw`, `vigr`, `ldapvi`, `shelldap`…) utilisent `/etc/alternatives/editor` qui est un lien symbolique vers l'éditeur par défaut du système. Sinon en ligne de commande on peut forcer la variable `EDITOR` :
2017-03-14 12:22:58 +01:00
~~~{.bash}
$ EDITOR=nano vipw
$ EDITOR=pico crontab -e
~~~
2023-06-27 00:43:25 +02:00
## Manipulations
2017-03-15 12:01:56 +01:00
2023-06-27 00:43:25 +02:00
### Déplacements et effacements
2020-03-23 08:37:57 +01:00
2023-06-27 00:43:25 +02:00
La plupart des terminaux/consoles utilisent les commandes [readline](https://fr.wikipedia.org/wiki/GNU_Readline).
Il est donc possible d'utiliser ses commandes pour faciliter le déplacement du curseur ou la suppression de caractères dans une ligne.
2020-03-23 08:37:57 +01:00
Déplacement :
2020-03-23 08:54:19 +01:00
2023-06-27 00:43:25 +02:00
* `Ctrl-e` : avancer à la fin de la ligne
* `Ctrl-f` : avancer d'un caractère
* `Ctrl-a` : revenir au début de la ligne
* `Ctrl-b` : revenir d'un caractère
* `Meta-b` : revenir au début du mot
* `Meta-f` : avancer à la fin du mot
2020-03-23 08:37:57 +01:00
Effacement :
2020-03-23 08:54:19 +01:00
2023-06-27 00:43:25 +02:00
* `Ctrl-k` : effacer jusqu'à la fin de la ligne
* `Ctrl-u` : effacer jusqu'au début de la ligne
* `Ctrl-w` : effacer jusqu'au début du mot
* `Meta-d` : effacer jusqu'à la fin du mot
2020-03-23 08:37:57 +01:00
2023-06-27 00:43:25 +02:00
> *Note* : Certaines lettres sont faciles à retenir : `e` pour End, `f` pour Forward, `b` pour Backward.
2020-03-23 08:37:57 +01:00
C'est aussi valable dans tous les outils utilisant la bibliothèque "readline", c'est à dire un grand nombre de logiciels.
2023-06-27 00:43:25 +02:00
### Tâches de fond
2022-07-25 10:54:47 +02:00
2023-06-27 00:43:25 +02:00
Jouons sur le fond :
2017-03-15 12:01:56 +01:00
~~~{.bash}
$ bg = "mettre en arrière plan"
$ fg = "mettre en premier plan"
$ jobs = "lister les tâches de fond"
%% = "dernier job utilisé (représenté par un +)"
%x = "job numéro x"
~~~
~~~{.bash}
$ vi foo
^Z
[1]+ Stopped vi foo
$ tail -f bar
...
^C
$ fg
^Z
[1]+ Stopped vi foo
$ kill -9 %%
[1]+ Killed vi foo
$ ( sleep 1m; echo "Premier !" ) &
[1] 13649
$ ( sleep 30; echo "Deuxième !" ) &
[2] 13651
$ jobs
2017-03-15 12:04:20 +01:00
[1]- Running sleep 1m && echo ...
[2]+ Running sleep 30 && echo ...
2017-03-15 12:01:56 +01:00
$ fg %2
^Z
2017-03-15 12:04:20 +01:00
[2]+ Stopped sleep 30 && echo ...
2017-03-15 12:01:56 +01:00
$ jobs
2017-03-15 12:04:20 +01:00
[1]- Running sleep 1m && echo ...
[2]+ Stopped sleep 30 && echo ...
2017-03-15 12:01:56 +01:00
$ sleep 30; bg
Premier !
2017-03-15 12:04:20 +01:00
[2]- Done sleep 30 && echo ...
2017-03-15 12:01:56 +01:00
Deuxième !
$ jobs
2017-03-15 12:04:20 +01:00
[1]- Done sleep 1m && echo ...
2017-03-15 12:07:25 +01:00
$ vi foo
^Z
[1]+ Stopped vi foo
$ exit
There are stopped jobs.
$ kill -9 %% #fg :x
$ exit
2017-03-15 12:39:56 +01:00
~~~
2023-06-27 00:43:25 +02:00
### Connaître le rang d'un élément dans une liste
2017-04-10 12:07:44 +02:00
Avec `grep` :
2017-04-10 12:07:44 +02:00
~~~
$ ./liste_serveur.sh | grep -n NOUVEAU_SERVEUR
2017-04-10 12:07:44 +02:00
2:NOUVEAU_SERVEUR
~~~
Avec `nl` :
~~~
$ ./liste_serveur.sh | nl | grep NOUVEAU_SERVEUR
2 NOUVEAU_SERVEUR
~~~
Avec `awk` :
~~~
$ ./liste_serveur.sh | awk '/NOUVEAU_SERVEUR/ { printf "%u\t%s\n", NR, $0 }'
2 NOUVEAU_SERVEUR
~~~
2023-06-27 00:43:25 +02:00
### Ajout mot en début de chaque ligne d'un buffer
2017-06-23 15:02:45 +02:00
2017-07-18 16:02:11 +02:00
~~~{.bash}
2023-02-06 10:23:36 +01:00
$ sed 's/^/Coucou /' <<<"$VAR"
2017-06-23 15:02:45 +02:00
~~~
### Extraire un bloc d'un fichier
2024-03-21 15:23:10 +01:00
Extraire un bloc d'un fichier, de l'expression régulière `<er-debut>` à l'expression régulière `<er-fin>`.
~~~{.bash}
sed -n '/<er-debut>/,/<er-fin>/p' <fichier>
~~~
Même chose en excluant `<er-fin>` :
~~~{.bash}
sed -n '/<er-debut>/ { x; :a; H; n; /<er-fin>/ {x;p;q}; ba }' <fichier>
~~~
2023-06-27 00:43:25 +02:00
### Avoir lempreinte SSH d'une liste de serveurs
2017-07-18 16:02:11 +02:00
2023-06-27 00:43:25 +02:00
Pour s'assurer que le host soit connu (`~/.ssh/known_hosts`) et ainsi automatiser des tâches sur des serveurs même si toujours aucune connexion effectuée et acceptée :
2017-07-18 16:02:11 +02:00
~~~{.bash}
$ (for host in machine1 machine2 ...; do echo $host; timeout -k 2 2 ssh -o 'StrictHostKeyChecking no' $host cat /etc/ssh/ssh_host_dsa_key.pub >> ~/.ssh/known_hosts; done)
~~~
2023-06-27 00:43:25 +02:00
### Manipuler lhistorique
<http://www.faqs.org/docs/bashman/bashref_109.html#SEC116>
2022-11-16 11:33:35 +01:00
Les éléments suivants permettent de rappeler des bouts des commandes précédentes. Voir la section [_History Expansion_(https://www.gnu.org/software/bash/manual/bash.html#History-Interaction) dans le manuel de Bash.
* `!!` : la dernière commande
* `!-2` : lavant dernière commande
* `!$` : le dernier argument de la dernière commande
* `!:2` : le deuxième argument de la dernière commande
* `^old^new^` : le dernière commande, en remplaçant `old` par `new`
2022-11-16 11:39:43 +01:00
> Lorsque quon entre une commande qui utilise l_history expansion_, Bash affiche la commande qui est vraiment exécutée juste après le _prompt_.
2022-11-16 11:33:35 +01:00
Exemple:
~~~
$ echo a b c
a b c
$ !! d
2022-11-16 11:33:35 +01:00
echo a b c d
a b c d
$ echo !$
echo d
d
$ !-3
echo a b c
a b c
$ # copier un fichier puis léditer
$ cp -p file file.old
$ vi !:2
vi file
$ # lister puis extraire le contenu dune archive
$ tar --list --file mon-archive.tar
[…]
$ ^list^extract^
tar --extract --file mon-archive.tar
2022-11-16 11:33:35 +01:00
~~~
2023-06-27 00:43:25 +02:00
## Fichiers et filesystems
2017-03-28 10:42:28 +02:00
2023-06-27 00:43:25 +02:00
### Ordinaire
2017-04-07 11:20:35 +02:00
2023-06-27 00:43:25 +02:00
#### Lister les répertoires montés sur le même FS
2017-09-18 10:42:00 +02:00
~~~{.bash}
2023-06-27 00:43:25 +02:00
$ ROOT=/; get_fs(){ df $1 | tail -n1 | awk '{print $1}'; }; fs_root="$(get_fs $ROOT)"; for file in $(ls -1 $ROOT); do [ "$(get_fs $ROOT$file)" = "$fs_root" ] && echo "$ROOT$file"; done
2017-09-18 10:42:00 +02:00
~~~
2023-06-27 00:43:25 +02:00
#### Savoir si lignes en doublon dans un fichier
2017-03-28 10:42:28 +02:00
~~~{.bash}
$ uniq -d <fichier>
~~~
ou autrement (appliquer un filtre différent) :
~~~{.bash}
$ diff <fichier> <(cat <fichier> | uniq)
~~~
2023-06-27 00:43:25 +02:00
#### Comparer deux fichiers quant à l'existence de nouvelles lignes
2017-03-28 10:42:28 +02:00
~~~{.bash}
$ grep -F -x -v -f <fichier1> <fichier2>
~~~
2023-06-27 00:43:25 +02:00
#### Supprimer des vieux fichiers
2017-03-15 12:39:56 +01:00
2023-06-27 00:43:25 +02:00
Si + vieux de 30 jours en modification :
2017-04-04 11:35:43 +02:00
2017-04-07 11:20:35 +02:00
~~~{.bash}
$ find DIR/ -type f -mtime +30 -delete
$ find DIR/ -type f -mtime +30 -exec rm '{}' \;
2022-08-26 17:21:52 +02:00
~~~
2023-06-27 00:43:25 +02:00
Si + vieux depuis le 24 aout 2022 à 11h :
2022-08-26 17:21:52 +02:00
~~~{.bash}
2022-08-26 17:44:38 +02:00
$ find DIR/ -type f ! -newermt "2022-08-24 11:00:00.00" -delete
2017-04-04 11:35:43 +02:00
~~~
2023-06-27 00:43:25 +02:00
#### Comparer deux fichiers à travers SSH
2017-04-07 11:43:55 +02:00
~~~{.bash}
$ diff <fichier> <(ssh REMOTE cat <fichier>)
~~~
2023-06-27 00:43:25 +02:00
#### Lister fichiers
2018-01-17 10:16:20 +01:00
Lister les fichiers dont le nom contient autre chose que des
lettres sans accent, des chiffres, des points ou des tirets :
~~~{.bash}
find /path/ | LC_ALL=C grep -Ev '^[a-zA-Z0-9./_-]+$'
~~~
> La variable `LC_ALL=C` évite de faire correspondre les accents, par exemple `é` et `e`.
2018-01-17 10:16:20 +01:00
Connaître la taille totale des derniers fichiers copiés :
~~~{.bash}
2018-03-29 10:12:20 +02:00
$ find /path/ -type f -mtime -1 -printf "'%p' " | xargs du -ch
2018-01-17 10:16:20 +01:00
~~~
2023-06-27 00:43:25 +02:00
#### Archiver
2023-02-06 10:50:15 +01:00
2023-06-27 00:43:25 +02:00
~~~{.bash}
2023-02-06 10:50:15 +01:00
tar --create --file archive.tar --verbose -- directory
tar --create --file archive.tar --exclude='directory/subdir' --verbose -- directory
tar --create --file archive.tar --exclude='directory/subdir/*.mp3' --verbose -- directory
~~~
> Lorsqu'on exclut un répertoire, il ne faut pas mettre de `/` à la fin du chemin.
2023-10-31 10:53:59 +01:00
#### Comparer deux fichiers avec des tabulations et des espaces :
Si un fichier est indenté avec le caractère tabulation (`\t`) et un autre avec 4 espaces :
~~~{.bash}
diff -Et --tabsize=4 <file1> <file2>
~~~
2023-06-27 00:43:25 +02:00
### Droits
#### Changer le propriétaire des fichiers selon le user
~~~{.bash}
$ find /path/ -user www-user -exec chown user: '{}' \;
~~~
2017-06-22 14:29:00 +02:00
2023-06-27 00:43:25 +02:00
#### Changer propriétaires owner et group selon actuels
2017-06-22 14:29:00 +02:00
~~~{.bash}
$ chown -c -R --from user:user userbis:userbis .
$ chown -c -R --from www-user:user www-userbis:userbis .
~~~
2023-06-27 00:43:25 +02:00
### Répertoire
2017-06-22 14:29:00 +02:00
2023-06-27 00:43:25 +02:00
#### Surveiller les ouvertures/écritures des fichiers présents dans un répertoire
2017-03-15 17:41:23 +01:00
~~~{.bash}
$ iwatch <target>
~~~
2023-06-27 00:43:25 +02:00
#### Savoir les différents users qui ont écrit dans /tmp
~~~{.bash}
2017-03-15 16:17:03 +01:00
$ stat -c %U /tmp/* | sort | uniq -c | sort -n
2017-03-16 10:37:53 +01:00
~~~
2023-06-27 00:43:25 +02:00
Si « too arguments pour stat » (version plus lente) :
2017-04-07 15:54:39 +02:00
2017-04-07 12:48:14 +02:00
~~~
$ find /tmp -exec stat -c %U '{}' \; | sort | uniq -c | sort -n
~~~
2023-06-27 00:43:25 +02:00
#### Comparer deux répertoires à travers SSH
2017-04-07 11:43:55 +02:00
Générique :
~~~{.bash}
$ DIR=$PWD
2017-04-07 11:46:43 +02:00
$ for file in $(rsync -rvn $DIR REMOTE:$DIR | grep -v "^skipping non-regular file" | head -n -2); do echo $DIR/$file :; diff $DIR/$file <(ssh REMOTE cat $DIR/$file); done
2017-04-07 11:43:55 +02:00
~~~
2023-06-27 00:43:25 +02:00
Pour `/etc/` :
2017-04-07 11:43:55 +02:00
~~~
# for file in $(rsync -rvn /etc/ --exclude=*.log --exclude=ssh --exclude=ssl --exclude=.git --exclude=shadow* --exclude=gshadow* REMOTE:/etc/ | \
2023-06-27 00:43:25 +02:00
grep -v "^skipping non-regular file" | head -n -2); do \
echo /etc/$file :; diff /etc/$file <(ssh REMOTE cat /etc/$file); done
2017-04-07 11:43:55 +02:00
~~~
2023-06-27 00:43:25 +02:00
### Espace disque
2017-04-07 11:20:35 +02:00
Attention, `tune2fs -l` [ne rapporte pas les bonnes valeurs d'inodes libres](https://bbs.archlinux.org/viewtopic.php?id=117301) lorsque le système de fichiers est monté.
2023-06-27 00:43:25 +02:00
#### Analyse disque
2017-04-07 11:20:35 +02:00
2023-06-27 00:43:25 +02:00
Quand il s'agit de `/` penser à exclure les autres partitions (si existante de toute évidence) :
2017-04-07 11:20:35 +02:00
~~~
2017-07-19 12:55:37 +02:00
# ncdu / --exclude /home --exclude /srv --exclude /var --exclude /tmp --exclude /boot --exclude /usr --exclude /proc
2017-04-07 11:20:35 +02:00
~~~
Pour certaines anciennes versions :
~~~
# ncdu --exclude "/home/*" /
~~~
2017-09-19 10:36:58 +02:00
ou plus simplement :
~~~
# du -chx / | sort -h | tail
~~~
Lister les dix plus gros fichiers réguliers sous `/home` :
~~~
# find /home -xdev -type f -print0 | du -h --files0-from=- | sort -hr | head
~~~
2017-04-07 11:20:35 +02:00
Sinon voir du côté de [HowtoDUC](/HowtoDUC).
2023-06-27 00:43:25 +02:00
#### Tester l'écriture disque
2017-03-16 10:37:53 +01:00
Simplement, en écriture (fichier de 5.1GB) :
~~~{.bash}
$ dd if=/dev/zero of=test count=10000000
2017-03-21 11:45:57 +01:00
~~~
2023-06-27 00:43:25 +02:00
#### Lister les répertoires ayant le plus de fichiers <=> max inode
2017-03-28 10:42:28 +02:00
2023-06-27 00:43:25 +02:00
##### À partir de Stretch (Debian 9)
2018-03-21 15:25:27 +01:00
2018-03-21 15:26:41 +01:00
On peut utiliser la commande *ncdu*, et lors du listage, appuyer sur la touche c (*sort by items*).
2023-06-27 00:43:25 +02:00
##### À partir de Jessie (Debian 8)
2018-03-21 15:20:41 +01:00
~~~{.bash}
2023-06-27 00:43:25 +02:00
$ du --inodes -x /path | sort -n
2018-03-21 15:20:41 +01:00
~~~
2023-06-27 00:43:25 +02:00
##### Autres versions
2018-03-21 15:26:41 +01:00
2018-03-21 15:25:27 +01:00
Sinon étapes par étapes (sans la commande *du --inodes*) :
2018-03-21 15:20:41 +01:00
2017-03-28 10:42:28 +02:00
~~~{.bash}
2017-04-20 17:31:16 +02:00
PATH_TO_WATCH='/var'; RESULT_FILE='list_max_inode.txt'; TMP=$(mktemp)
2017-03-28 10:42:28 +02:00
#Regarder dans le premier niveau
2018-03-21 11:09:34 +01:00
#OLD : find $PATH_TO_WATCH -type d -printf '%p\n' | sed 's/"/\\"/g' | sed 's/^\(.*\)$/"\1"/' | while read i; do echo $(echo $i | xargs ls -a | wc -l) $i; done | sort -n > $TMP
2018-06-22 10:28:49 +02:00
#OLD : export IFS="
2018-06-22 10:29:32 +02:00
#"; for i in $(find $PATH_TO_WATCH -type d); do echo $(ls -a | wc -l) $i; done | sort -n > $TMP
2018-06-22 10:28:49 +02:00
for i in $(du -x $PATH_TO_WATCH | awk '{print $2}'); do echo $(ls -1 -a $i| wc -l) $i; done | sort -n > $TMP
2018-03-21 11:09:34 +01:00
2017-03-28 10:42:28 +02:00
#compter dans les sous niveaux
cat $TMP | (while read line; do num=$(echo $line | awk '{ print $1 }'); path=$(echo $line | awk '{ print $2 }'); echo ${path%/*}; done) | sort | uniq | (while read line; do echo $(grep "$line" $TMP | cut -f1 -d' ' | xargs echo -n | tr -s ' ' '+' | xargs echo | bc -l) $line; done) | sort -n | tee $RESULT_FILE
rm $TMP
~~~
2023-06-27 00:43:25 +02:00
#### Comprendre pourquoi résultat d'un `df` ne correspond pas un `du`
2018-05-09 15:50:23 +02:00
Si le résultat d'un `df` indique une occupation disque plus importante que lorsque on fait un `du -cx /to/path`, cela veut dire que sans doute un fichier a été supprimé mais est encore en lecture par un process.
2023-06-27 00:43:25 +02:00
2018-05-09 15:50:23 +02:00
On peut le rechercher en faisant :
2023-06-27 00:43:25 +02:00
~~~{.bash}
2018-05-09 15:50:23 +02:00
# lsof /var/ | grep deleted
~~~
2023-06-27 00:43:25 +02:00
Ce qui équivaut à :
2018-05-09 15:50:23 +02:00
~~~{}
# lsof -Fn | grep ^n/var/ | sed 's/^n//' | xargs -n1 -I file stat file | grep 'No such file or directory'
~~~
2023-06-27 00:43:25 +02:00
En tuant le process, la place sur le disque devrait se « libérer » et être de nouveau disponible.
2018-05-09 15:50:23 +02:00
2023-06-27 00:43:25 +02:00
## Utilisateurs UNIX
2017-04-05 14:42:17 +02:00
2023-06-27 00:43:25 +02:00
### Créer HOME
2018-02-01 11:46:19 +01:00
Si l'utilisateur existe, et qu'il est nécessaire de créer un répertoire $HOME pour ce dernier (car non existant) :
~~~{.bash}
# mkhomedir_helper user
~~~
2018-02-01 11:46:51 +01:00
Va appliquer les bon droits sur fichiers/répertoires + copier ce qu'il y a dans /etc/skel.
2018-02-01 11:46:19 +01:00
2023-06-27 00:43:25 +02:00
### Lister les utilisateurs + groupe
2017-04-05 14:42:17 +02:00
2023-06-27 00:43:25 +02:00
#### UNIX
2017-04-05 14:48:22 +02:00
2017-04-05 14:42:17 +02:00
~~~{.bash}
$ for user in $(getent passwd | awk -F ':' '$3 > 10000 {printf $1 " "}'); do groups $user; done
~~~
2023-06-27 00:43:25 +02:00
#### LDAP
2017-04-05 14:48:22 +02:00
2017-04-05 15:58:31 +02:00
~~~{.bash}
2017-04-05 15:58:47 +02:00
$ for user in $(getent passwd | awk -F ':' '$3 > 10000 {printf $1 " "}'); do \
2023-06-27 00:43:25 +02:00
ldapsearch -x -h localhost -LLL -b "ou=people,dc=example,dc=com" cn | tail -n2 | \
tr '\n' ' ' | cut -d':' -f2 | echo -n "$(cat <&0)"; echo = $(groups $user); done
2017-04-05 15:58:31 +02:00
~~~
2017-04-05 14:48:22 +02:00
2023-06-27 00:43:25 +02:00
### Lister les expirations des mots de passe utilisateurs par date
2023-06-27 00:43:25 +02:00
#### LDAP
On se sert pour ça du champ `sambaPwdLastSet` indiquant la date du dernier changement et on y ajoute 200 jours (17280000 secondes) correspondant au champ `shadowMax` indiquant la durée de validité d'un mot de passe. On n'affiche pas les utilisateurs ayant leur mot de passe déjà expirés depuis plus d'un mois.
~~~{.bash}
#!/bin/bash
delete_before=$(date "+%Y %b" -d -1month)
/usr/bin/ldapsearch -x -b 'ou=people,dc=XXXXXXX,dc=com' | /bin/sed '/sambaPwdLastSet\|uid:/!d' | /usr/bin/awk 'NR%2{printf "%s ",$0;next;}1' | /bin/sed -e 's/uid: //' -e 's/sambaPwdLastSet: //' | /usr/bin/xargs -L1 bash -c 'echo $(($1+17280000)); echo $0' | /usr/bin/awk 'NR%2{printf "%s ",$0;next;}1' | /usr/bin/xargs -L1 bash -c 'date -d @$0; echo $1' | /usr/bin/awk 'NR%2{printf "%s ",$0;next;}1' | /bin/sed -e "s/^\(.*\)\ \(20..\)\ \(.*\)$/- \2 \1 \3/" | /bin/sed -r -e 's/(\s+)?\S+//3' -e 's/(\s+)?\S+//6' | /usr/bin/sort -n -k 2 -k 3M -k 4 | /usr/bin/awk "/$delete_before/{p=1}p"
~~~
2023-06-27 00:43:25 +02:00
## Serveur web
2017-03-28 10:42:28 +02:00
2023-06-27 00:43:25 +02:00
### Avoir un rendu des requêtes par IP à partir d'un access.log
2017-03-21 11:45:57 +01:00
2017-05-02 10:51:45 +02:00
Selon le format, il faudra peut-être changer la valeur du field de cut (-f1 par -f2).
2023-06-27 00:43:25 +02:00
#### Compte rendu pour un laps de temps
2017-03-21 12:07:28 +01:00
2017-04-13 11:30:57 +02:00
~~~{.bash}
$ date; (timeout 60 tail -f access.log | cut -d' ' -f1) | sort | uniq -c | sort -n
2017-03-21 12:07:28 +01:00
~~~
2023-06-27 00:43:25 +02:00
#### En direct
2017-03-21 12:07:28 +01:00
2017-03-21 16:44:00 +01:00
- Version simple :
2017-04-13 11:30:57 +02:00
~~~{.bash}
$ tail -f access.log | stdbuf -oL cut -d ' ' -f1 | uniq -c
2017-03-21 16:44:00 +01:00
~~~
- Version couleur :
2017-04-13 11:30:57 +02:00
~~~{.bash}
$ SEUIL=5; tail -f access.log | stdbuf -oL cut -d ' ' -f1 | stdbuf -oL uniq -c | \
2017-03-21 16:44:00 +01:00
eval "awk '\$1 > $SEUIL {printf \"\\033[1;31m\" \$1 \" \" \$2 \"\\033[0m \\n\"; next;};{printf \$1 \" \" \$2 \"\\n\";}'"
2017-03-21 17:24:13 +01:00
~~~
2023-06-27 00:43:25 +02:00
### Comparer les requêtes effectués dans access.log
2017-04-11 10:57:19 +02:00
Les différentes requêtes sont comparés aux nombres de caractères différent. La variable SEUIL est la limite de caractères différents pour 2 requêtes.
~~~{.bash}
2023-06-27 00:43:25 +02:00
$ SEUIL=20; i=1; lastline=; cat access.log | sed 's/.*\] \(.*\)\" [0-9]\{3\}.*$/\1\"/' | \
(while read line; do diff=$(cmp -bl <(echo "$lastline") <(echo "$line") 2>/dev/null | wc -l); \
((diff<SEUIL)) && ((i=i+1)) || { echo "$i $line"; i=1; }; lastline="$line"; done)
2017-04-11 10:57:19 +02:00
~~~
2023-06-27 00:43:25 +02:00
Si on veut les adresses IP, ou simplement trier le access.log avant l'analyse, il faut modifier après le : cat acccess.log |
2017-04-11 10:57:19 +02:00
2017-04-13 11:27:18 +02:00
Et si l'on considère les mots comme une seule différence (et non par caractères), on peut descendre le seuil :
~~~{.bash}
2023-06-27 00:43:25 +02:00
$ SEUIL=3; i=1; lastline=; cat access.log | sed 's/.*\] \(.*\)\" [0-9]\{3\}.*$/\1\"/' | \
(while read line; do diff=$(cmp -bl <(echo "$lastline") <(echo "$line") 2>/dev/null | awk '{print $1}' | \
(compt=0; lastnumber=0; while read number; do ((lastnumber+1!=number)) && ((compt=compt+1)); lastnumber=$number; done; echo $compt)); \
((diff<SEUIL)) && ((i=i+1)) || { echo "$i $line"; i=1; }; lastline="$line"; done)
2017-04-13 11:27:18 +02:00
~~~
2023-06-27 00:43:25 +02:00
C'est à dire qu'entre 2 requêtes du type :
2017-04-13 11:27:18 +02:00
2023-06-27 00:43:25 +02:00
- `/page.do?pseudo=Example&pass=0322`
- `/page.do?pseudo=Mail&pass=3892`
2017-04-13 11:27:18 +02:00
2023-06-27 00:43:25 +02:00
il n'y a que 2 différences : le « pseudo », et le « pass ».
2017-04-13 11:27:18 +02:00
2023-06-27 00:43:25 +02:00
### Analyser un apache-status
2017-10-12 15:12:13 +02:00
Compter le nombre de requêtes par IPs :
~~~{.bash}
cat FICHIER | grep --color '<td>[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+</td>' | sed 's/.*<td>\([0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+\)<\/td>.*/\1/g' | sort -n | uniq -c | sort -n
~~~
Le faire pour un créneau horaire :
~~~{.bash}
$ cd /var/www/apache-status
$ DATE=2017-10-12-14-
$ cat ${DATE}*.html | grep --color '<td>[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+</td>' | sed 's/.*<td>\([0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+\)<\/td>.*/\1/g' | sort -n | uniq -c | sort -n
~~~
2023-06-27 00:43:25 +02:00
## Processus / Process
2017-03-28 10:42:28 +02:00
2024-03-11 10:43:05 +01:00
### Pour un utilisateur
~~~{.bash}
pgrep -u <user1> | xargs -r ps -F
~~~
2023-06-27 00:43:25 +02:00
### Surveiller les nouveaux processus créés
2017-04-06 12:35:32 +02:00
2023-06-27 00:43:25 +02:00
#### Liste simple
2017-04-06 12:35:32 +02:00
~~~{.bash}
2024-03-11 10:43:58 +01:00
$ ps -e -o etimes=,pid,cmd | sort -rn | awk '$1 != 0 && $3 !~ /^\[/'
2017-04-06 12:35:32 +02:00
~~~
2023-06-27 00:43:25 +02:00
#### il y a moins de X minutes
2017-09-26 09:59:50 +02:00
~~~{.bash}
$ MIN=5; ps -e -o etimes=,pid,cmd | sort -rn | awk '{if ($1<'$(( MIN * 60 ))' && $1>0 && $3!~/\[.*\]/) print $0 }'
~~~
2023-06-27 00:43:25 +02:00
#### Watch
2017-04-06 12:47:43 +02:00
2017-04-06 12:53:57 +02:00
Toutes les 5 secondes :
2017-04-06 12:47:43 +02:00
~~~{.bash}
2017-04-18 12:23:42 +02:00
$ watch -n 5 -d "ps -e -o etimes=,pid,user,cmd | sort -n | awk '{if (\$1==0 || \$2==$$ || \$3~/watch/ || \$3~/\[.*\]/) {} else print \$0 }'"
2017-04-18 16:43:26 +02:00
~~~
Se baser seulement par rapport aux utilisateurs ayant créés dernièrement ces processus (SEUIL <=> processus vieux de moins de $SEUIL secondes) :
~~~{.bash}
$ SEUIL=100; watch -n 5 -d "ps -e -o etimes=,user | sort -n | awk '{if (\$1<$SEUIL) print \$2 }' | sort | uniq -c | sort -n"
2017-05-02 18:02:26 +02:00
~~~
2023-06-27 00:43:25 +02:00
#### Cron
2018-04-19 14:43:12 +02:00
Recevoir le output de la commande *top* en cron :
~~~{.bash}
top -b -d 1
~~~
2023-06-27 00:43:25 +02:00
#### Lister avec le plus de fils (/fork)
2017-05-02 18:02:26 +02:00
~~~{.bash}
(total_procs=0; for foo in $(ps -e -o ppid | sed '1d' | sort -n | uniq -c | sort -n | awk '{ print $1 ":" $2 }'); do val=$(echo $foo | cut -d: -f1); total_procs=$((total_procs+val)); pid=$(echo $foo | cut -d: -f2); (( pid != 0 )) && { echo -n $val ') '; ps -p $pid -o pid,cmd | tail -n1; }; done; echo Total = $total_procs) | tail
2017-05-04 16:41:59 +02:00
~~~
2023-06-27 00:43:25 +02:00
#### Consommation Swap
2017-05-05 10:46:22 +02:00
~~~{.bash}
(for file in /proc/*; do [ -e $file/status ] || continue; PID=$(basename $file); RES=$(grep VmSwap: $file/status | sed 's/VmSwap\:[[:space:]]*\(.*\)/\1/'); [ -n "$RES" ] && echo $RES ' = ' $PID ' ' $(ps -p $PID -o cmd --no-headers); done) | sort -n
~~~
2023-06-27 00:43:25 +02:00
#### Consommation RAM (VmSize)
2017-06-19 11:27:09 +02:00
2017-08-31 11:11:28 +02:00
~~~{.bash}
$ top -o RES
~~~
2023-06-27 00:43:25 +02:00
#### Par process
2018-05-23 11:16:02 +02:00
~~~{.bash}
$ ps o user:20,pid,pcpu,pmem,vsz,rss,tty,stat,start,time,comm -p 3108
~~~
2023-06-27 00:43:25 +02:00
#### Par utilisateur
2017-06-19 11:27:09 +02:00
~~~{.bash}
2017-07-26 13:06:34 +02:00
for var in users1 users2; do echo '#' $var ':'; ps -u $var -o pid= | while read pid; do echo -n $pid ') threads: ' "$(ps -p $pid -T | wc -l)" "; $(grep VmSize /proc/$pid/status)"; echo; vmsize=$(grep VmSize /proc/$pid/status | sed 's/^VmSize:\s*\([0-9]*\) kB/\1/'); done; done
2017-08-31 11:10:06 +02:00
for var in $(getent passwd | cut -d':' -f1); do echo '#' $var ':'; ps -u $var -o pid= | while read pid; do echo -n $pid ') threads: ' "$(ps -p $pid -T | wc -l)" "; $(grep VmSize /proc/$pid/status)"; echo; vmsize=$(grep VmSize /proc/$pid/status | sed 's/^VmSize:\s*\([0-9]*\) kB/\1/'); done; done
2017-06-19 11:27:09 +02:00
~~~
2017-07-18 13:51:04 +02:00
## Fichiers ouverts
2017-05-04 16:41:59 +02:00
2017-05-05 10:46:22 +02:00
### socket/port
- Connaître les sockets ouvertes et ports en écoutent par un processus :
~~~{.bash}
$ lsof -Pan -p PID -i
~~~
- Connaître le pid qui écoute sur un port (2ème colonne) :
~~~{.bash}
$ lsof -i :Port
~~~
2017-05-04 16:42:24 +02:00
### Selon l'utilisateur
2017-05-04 16:41:59 +02:00
2023-06-27 00:43:25 +02:00
~~~{.bash}
2017-05-04 16:41:59 +02:00
# lsof -u UID
~~~
Si www-data a uid=33, lister fichiers ouvert par serveur-web :
2023-06-27 00:43:25 +02:00
~~~{.bash}
2017-05-04 16:41:59 +02:00
# lsof -u 33 | awk '{ print $2 " = " $9 }' | grep "/home/.*$"
2017-05-26 10:41:16 +02:00
~~~
## Créer un fichier "dummy" de différentes tailles
Pour tester les performances d'un FTP ou autre on a besoin d'envoyer un fichier volumineux ou pas, voici comment créer un fichier vide (dummy) d'une certaine taille avec dd :
Pour un fichier de 10Mio :
2023-06-27 00:43:25 +02:00
~~~{.bash}
$ dd if=/dev/zero of=10M.bin bs=1024 count=0 seek=$[1024*10]
2017-05-26 10:41:16 +02:00
~~~
Pour 100Mio :
2023-06-27 00:43:25 +02:00
~~~{.bash}
$ dd if=/dev/zero of=100M.bin bs=1024 count=0 seek=$[1024*100]
2017-05-26 10:41:16 +02:00
~~~
Pour 1Gio :
2023-06-27 00:43:25 +02:00
~~~{.bash}
$ dd if=/dev/zero of=1G.bin bs=1024 count=0 seek=$[1024*1024]
2017-07-18 13:51:04 +02:00
~~~
2023-06-27 00:43:25 +02:00
### stdout / stderr
2017-07-18 13:51:04 +02:00
Renvoyer stdout/stderr dans une même sortie (par exemple /dev/null) :
2023-06-27 00:43:25 +02:00
~~~{.bash}
$ eject /dev/coin > /dev/null 2>&1
2017-08-20 04:33:33 +02:00
~~~
Détecter les tabulations
2023-06-27 00:43:25 +02:00
~~~{.bash}
2017-08-20 04:33:33 +02:00
$ grep -P '\t'
2017-09-25 09:53:44 +02:00
~~~
2023-06-27 00:43:25 +02:00
### Conflit sur `stdin`
2022-09-07 15:31:46 +02:00
Rediriger `stdin` peut être nécéssaire, par exemple, lorsquon combine une boucle `while read`, un _here-document_ et des commandes interactives.
Dans la boucle suivante, lorsquon rentre dans la boucle, la variable `hostname` contient bien «server1».
2023-06-27 00:43:25 +02:00
~~~{.bash}
2022-09-07 15:31:46 +02:00
while read hostname
do
ssh "${hostname}"
done << eof
server1
server2
eof
~~~
On peut sattendre à ce quau prochain tour de boucle, `hostname` contienne «server2», mais lorsque la commande `ssh` sera exécutée durant le premier passage de la boucle, cest bien `ssh` qui va consommer la ligne «server2»! Pour empêcher ça, on peut faire lire un autre descripteur de fichier, comme `3`, à `read` et envoyer le _here-document_ dessus:
2023-06-27 00:43:25 +02:00
~~~{.bash}
2022-09-07 15:31:46 +02:00
while read hostname 0<&3
do
ssh "${hostname}"
done 3<< eof
server1
server2
eof
~~~
On peut aussi faire lire au autre descripteur de fichier à `ssh` à la place:
2023-06-27 00:43:25 +02:00
~~~{.bash}
2022-09-07 15:31:46 +02:00
while read hostname
do
ssh "${hostname}" 0<&3
done 3<&0 << eof
server1
server2
eof
~~~
2023-08-31 11:21:11 +02:00
Dans le cas particulier de `ssh` il y aussi l'option `-n` qui produit le même résultat de manière plus lisible :
~~~{.bash}
while read hostname
do
ssh -n "${hostname}"
done << eof
server1
server2
eof
~~~
2023-06-27 00:43:25 +02:00
## Serveur mail
2017-09-25 09:53:44 +02:00
2023-06-27 00:43:25 +02:00
### Avoir vision des différentes erreurs mailq (MAILER-DAEMON)
2017-09-25 09:53:44 +02:00
~~~{.bash}
(for id in $(mailq | grep MAILER\-DAEMON | cut -d' ' -f1); do postcat -q $id| grep Diagnostic\-Code\:; done) | sort | uniq -c | sort -n
2017-11-03 13:59:11 +01:00
~~~
2018-09-19 19:56:48 +02:00
2023-06-27 00:43:25 +02:00
## Parsing
2018-09-19 19:56:48 +02:00
2023-06-27 00:43:25 +02:00
### JSON avec jq
2018-09-19 19:56:48 +02:00
jq est un puissant outil de manipulation de JSON en cli. Il va aussi mettre en forme et colorer en fonction du terminal.
~~~
# apt install jq
$ curl --silent ipinfo.io | jq
~~~
On peut s'en servir pour extraire certaine partie du JSON :
* .[] : Addresser une entrée d'un tableau. Exemple ".[0]" pour la première entrée
* .foo : Récupérer la valeur de la clée foo
2023-06-27 00:43:25 +02:00
Exemple : récupérer l'IP d'un container sur l'interface `docker_gwbridge` :
2018-09-19 19:56:48 +02:00
~~~
# docker inspect docker_gwbridge | jq ".[0].Containers.f37ac628a4630da4aabbd23ba8eebf9c72dce5f3ba03675515a8b3619f8425d2.IPv4Address"
~~~
2023-06-27 00:43:25 +02:00
> *Note* : Pour faire des tests ou s'entrainer : <https://jqplay.org/>
2018-09-19 19:56:48 +02:00
2023-06-27 00:43:25 +02:00
## Mot de passe
2023-06-27 00:43:25 +02:00
~~~{.bash}
apg -c /dev/urandom -a0 -n1 -m20 -MSNCL -E oOlL10\&\\\/\"\'
~~~
2023-06-27 00:43:25 +02:00
Sans la commande `apg` :
2023-06-27 00:43:25 +02:00
~~~{.bash}
$ tr -cd [:alnum:] < /dev/urandom | head -c22; echo
~~~
> La commande `echo` à la fin permet davoir un saut de ligne. Sinon, le _prompt_ sera collé au mot de passe qui a été généré.
2023-06-27 00:43:25 +02:00
### En hexadecimal
Pour générer 12 charactères :
2023-06-27 00:43:25 +02:00
~~~{.bash}
$ openssl rand -hex 6
~~~
2023-06-22 17:50:52 +02:00
Sans la commande `openssl` :
~~~{.bash}
tr -cd a-f0-9 < /dev/urandom | head -c12; echo
~~~
2023-06-27 00:43:25 +02:00
## Variables
2023-06-22 17:50:52 +02:00
2023-06-27 00:43:25 +02:00
Les variables d'environnement actuelles :
2023-06-22 17:50:52 +02:00
2023-06-27 00:43:25 +02:00
~~~{.bash}
$ printenv
~~~
Ajouter une variable :
~~~{.bash}
$ FOO=bar
~~~
Ajouter une variable d'environnement :
~~~{.bash}
$ export FOO=bar
~~~
Supprimer une variable :
~~~{.bash}
$ unset FOO
~~~
Attention, quand on utilise une variable temporaire dans une ligne de commande, elle n'est utilisée
que pour l'environnement et ne peut être utiliser comme une variable. Ainsi :
~~~{.bash}
$ BAR=bar echo -n foo $BAR ; BAZ=baz env | grep baz
fooBAZ=baz
~~~
## Divers
### Hexadécimal - décimal
~~~{.bash}
2023-06-22 17:50:52 +02:00
$ printf '%x\n' 27
1b
$ printf '%d\n' 0x1b
27
2023-06-27 00:43:25 +02:00
~~~
2023-06-22 17:50:52 +02:00
2023-06-27 00:43:25 +02:00
~~~{.bash}
2023-06-22 17:50:52 +02:00
$ bc
> obase=16
> 27
1B
2023-06-27 00:43:25 +02:00
~~~
2023-06-22 17:50:52 +02:00
2023-06-27 00:43:25 +02:00
~~~{.bash}
2023-06-22 17:50:52 +02:00
$ bc
> ibase=16
> 1B
27
2023-06-27 00:43:25 +02:00
~~~
2024-03-04 17:04:35 +01:00
### Tester une connexion TCP ou UDP
Si on a pas [`telnet(1)`](https://manpages.debian.org/bullseye/telnet/telnet.1.en.html) ou [`netcat(1)`](https://manpages.debian.org/bullseye/netcat-openbsd/netcat.1.en.html) sous la main, on peut utiliser `/dev/tcp` et `/dev/udp`. Par exemple, pour tester une connexion à Redis :
~~~{.bash}
$ exec 3<> /dev/tcp/127.0.0.1/6379
$ echo quit >&3
$ cat <&3
+OK
~~~
Un autre exemple avec HTTP :
~~~{.bash}
$ exec 3<> /dev/tcp/example.com/80
$ printf 'GET /index.html HTTP/1.1\r\nHost: example.com\r\n\r\n' >&3
$ cat <&3
HTTP/1.1 200 OK
[...]
~~~
Dans ces exemples, on écrit avec une commande, puis on lit avec une autre. Pour avoir un semblant d'interactivité, on peut exécuter le processus qui lit la réponse en arrière plan avant d'écrire. Par exemple avec Redis :
~~~{.bash}
$ exec 3<> /dev/tcp/127.0.0.1/6379
$ cat <&3 & cat >&3
[1] 31415
auth REDACTED
+OK
quit
+OK
^C
[1]+ Done cat 0<&3
~~~