466 lines
12 KiB
Markdown
466 lines
12 KiB
Markdown
Quelques astuces SHell. Des bouts de commandes, ou simplement les arguments qui vont bien.
|
|
|
|
# Script
|
|
|
|
Style guide : <https://google.github.io/styleguide/shell.xml>
|
|
|
|
# Configuration
|
|
|
|
## Initialisation du shell
|
|
|
|
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>
|
|
|
|
## History Bash
|
|
|
|
`bashrc` :
|
|
|
|
~~~
|
|
export HISTCONTROL=$HISTCONTROL${HISTCONTROL+,}ignoreboth
|
|
export HISTSIZE=65535
|
|
export HISTTIMEFORMAT="%c : "
|
|
~~~
|
|
|
|
> *Note* : si besoin on peut `chattr +a /root/.bash_history` pour empêcher sa suppression
|
|
|
|
## Changer l'éditeur de texte par défaut pour une commande
|
|
|
|
Normalement le script (vipw, vigr, -ldapvi, ...) se réfère aux fichiers scripts (qui ne sont que des liens vers les binaires) :
|
|
|
|
~~~{.bash}
|
|
/etc/alternatives/
|
|
~~~
|
|
|
|
Sinon en ligne de commande :
|
|
|
|
~~~{.bash}
|
|
$ EDITOR=nano vipw
|
|
~~~
|
|
|
|
# Manipulations
|
|
|
|
## Tâches de fond
|
|
|
|
~~~{.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
|
|
[1]- Running sleep 1m && echo ...
|
|
[2]+ Running sleep 30 && echo ...
|
|
$ fg %2
|
|
^Z
|
|
[2]+ Stopped sleep 30 && echo ...
|
|
$ jobs
|
|
[1]- Running sleep 1m && echo ...
|
|
[2]+ Stopped sleep 30 && echo ...
|
|
$ sleep 30; bg
|
|
Premier !
|
|
[2]- Done sleep 30 && echo ...
|
|
Deuxième !
|
|
$ jobs
|
|
[1]- Done sleep 1m && echo ...
|
|
$ vi foo
|
|
^Z
|
|
[1]+ Stopped vi foo
|
|
$ exit
|
|
There are stopped jobs.
|
|
$ kill -9 %% #fg :x
|
|
$ exit
|
|
~~~
|
|
|
|
## Connaître le rang d'un élément dans une liste
|
|
|
|
Utilisation de **tac** pour inverser la liste, sans la trier :
|
|
|
|
~~~{.bash}
|
|
$ ./liste_serveur.sh | tac | grep -n NOUVEAU_SERVEUR
|
|
2:NOUVEAU_SERVEUR
|
|
~~~
|
|
|
|
-> 'grep -n' reviens au même que 'nl | grep'
|
|
|
|
## Ajout mot en début de chaque ligne d'un buffer
|
|
|
|
~~~{.bash}
|
|
$ sed 's/^/Coucou /g' <<<"$VAR"
|
|
~~~
|
|
|
|
## Avoir le 'fingerprint' SSH d'une liste de serveur
|
|
|
|
-> 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 :
|
|
|
|
~~~{.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)
|
|
~~~
|
|
|
|
# Fichiers et FS
|
|
|
|
## Ordinaire
|
|
|
|
### Lister les répertoires monté sur le même FS
|
|
|
|
~~~{.bash}
|
|
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
|
|
~~~
|
|
|
|
### Savoir si lignes en doublon dans un fichier
|
|
|
|
~~~{.bash}
|
|
$ uniq -d <fichier>
|
|
~~~
|
|
|
|
ou autrement (appliquer un filtre différent) :
|
|
|
|
~~~{.bash}
|
|
$ diff <fichier> <(cat <fichier> | uniq)
|
|
~~~
|
|
|
|
### Comparer deux fichiers quant à l'existence de nouvelles lignes
|
|
|
|
~~~{.bash}
|
|
$ grep -F -x -v -f <fichier1> <fichier2>
|
|
~~~
|
|
|
|
### Supprimer des vieux fichiers
|
|
|
|
- Par exemple, si + vieux de 30 jours en modification :
|
|
|
|
~~~{.bash}
|
|
$ find DIR/ -type f -mtime +30 -delete
|
|
$ find DIR/ -type f -mtime +30 -exec rm '{}' \;
|
|
~~~
|
|
|
|
### Comparer deux fichiers à travers SSH
|
|
|
|
~~~{.bash}
|
|
$ diff <fichier> <(ssh REMOTE cat <fichier>)
|
|
~~~
|
|
|
|
## Droits
|
|
|
|
### Changer propriétaires owner et group selon actuels
|
|
|
|
~~~{.bash}
|
|
$ chown -c -R --from user:user userbis:userbis .
|
|
$ chown -c -R --from www-user:user www-userbis:userbis .
|
|
~~~
|
|
|
|
### Copier seulement les droits de deux hiérarchies de fichiers
|
|
|
|
## Répertoire
|
|
|
|
### Surveiller les ouvertures/écriture des fichiers présent dans un répertoire
|
|
|
|
~~~{.bash}
|
|
$ iwatch <target>
|
|
~~~
|
|
|
|
### Savoir les différents users qui ont écris dans /tmp
|
|
|
|
~~~{.bash}
|
|
$ stat -c %U /tmp/* | sort | uniq -c | sort -n
|
|
~~~
|
|
|
|
Si «too arguments pour stat» (version bien plus lente) :
|
|
|
|
~~~
|
|
$ find /tmp -exec stat -c %U '{}' \; | sort | uniq -c | sort -n
|
|
~~~
|
|
|
|
### Comparer deux répertoires à travers SSH
|
|
|
|
Générique :
|
|
|
|
~~~{.bash}
|
|
$ DIR=$PWD
|
|
$ 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
|
|
~~~
|
|
|
|
Pour /etc/ :
|
|
|
|
~~~
|
|
# for file in $(rsync -rvn /etc/ --exclude=*.log --exclude=ssh --exclude=ssl --exclude=.git --exclude=shadow* --exclude=gshadow* REMOTE:/etc/ | \
|
|
grep -v "^skipping non-regular file" | head -n -2); do \
|
|
echo /etc/$file :; diff /etc/$file <(ssh REMOTE cat /etc/$file); done
|
|
~~~
|
|
|
|
## Espace et Inode
|
|
|
|
### Analyse disque
|
|
|
|
Quand il s'agit de / - penser à exclure les autres partitions (si existante de toute évidence) :
|
|
|
|
~~~
|
|
# ncdu / --exclude /home --exclude /srv --exclude /var --exclude /tmp --exclude /boot --exclude /usr --exclude /proc
|
|
~~~
|
|
|
|
Pour certaines anciennes versions :
|
|
|
|
~~~
|
|
# ncdu --exclude "/home/*" /
|
|
~~~
|
|
|
|
ou plus simplement :
|
|
|
|
~~~
|
|
# du -chx / | sort -h | tail
|
|
~~~
|
|
|
|
Sinon voir du côté de [HowtoDUC](/HowtoDUC).
|
|
|
|
### Tester l'écriture disque
|
|
|
|
Simplement, en écriture (fichier de 5.1GB) :
|
|
|
|
~~~{.bash}
|
|
$ dd if=/dev/zero of=test count=10000000
|
|
~~~
|
|
|
|
### Lister les répertoires ayant le plus de fichiers <=> max inode
|
|
|
|
~~~{.bash}
|
|
PATH_TO_WATCH='/var'; RESULT_FILE='list_max_inode.txt'; TMP=$(mktemp)
|
|
#Regarder dans le premier niveau
|
|
(for i in $(find $PATH_TO_WATCH -type d); do echo $(ls -a "$i" | wc -l) $i; done) | sort -n > $TMP
|
|
#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
|
|
~~~
|
|
|
|
# Utilisateurs UNIX
|
|
|
|
## Lister les utilisateurs + groupe
|
|
|
|
### UNIX
|
|
|
|
~~~{.bash}
|
|
$ for user in $(getent passwd | awk -F ':' '$3 > 10000 {printf $1 " "}'); do groups $user; done
|
|
~~~
|
|
|
|
### LDAP
|
|
|
|
~~~{.bash}
|
|
$ for user in $(getent passwd | awk -F ':' '$3 > 10000 {printf $1 " "}'); do \
|
|
ldapsearch -x -h localhost -LLL -b "dc=MACHINE,dc=DOMAIN,dc=COM" cn | tail -n2 | \
|
|
tr '\n' ' ' | cut -d':' -f2 | echo -n "$(cat <&0)"; echo = $(groups $user); done
|
|
~~~
|
|
|
|
# Serveur web
|
|
|
|
## Avoir un rendu des requêtes par IP à partir d'un access.log
|
|
|
|
Selon le format, il faudra peut-être changer la valeur du field de cut (-f1 par -f2).
|
|
|
|
### Compte rendu pour un laps de temps
|
|
|
|
~~~{.bash}
|
|
$ date; (timeout 60 tail -f access.log | cut -d' ' -f1) | sort | uniq -c | sort -n
|
|
~~~
|
|
|
|
### En direct
|
|
|
|
- Version simple :
|
|
|
|
~~~{.bash}
|
|
$ tail -f access.log | stdbuf -oL cut -d ' ' -f1 | uniq -c
|
|
~~~
|
|
|
|
- Version couleur :
|
|
|
|
~~~{.bash}
|
|
$ SEUIL=5; tail -f access.log | stdbuf -oL cut -d ' ' -f1 | stdbuf -oL uniq -c | \
|
|
eval "awk '\$1 > $SEUIL {printf \"\\033[1;31m\" \$1 \" \" \$2 \"\\033[0m \\n\"; next;};{printf \$1 \" \" \$2 \"\\n\";}'"
|
|
~~~
|
|
|
|
## Comparer les requêtes effectués dans access.log
|
|
|
|
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}
|
|
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)
|
|
~~~
|
|
|
|
Si on veut les adresses IPs, ou simplement trier le access.log avant l'analyse, il faut modifier après le : cat acccess.log |
|
|
|
|
Et si l'on considère les mots comme une seule différence (et non par caractères), on peut descendre le seuil :
|
|
|
|
~~~{.bash}
|
|
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)
|
|
~~~
|
|
|
|
C'est à dire que entre 2 requêtes du type :
|
|
|
|
- /page.do?pseudo=Example&pass=0322
|
|
- /page.do?pseudo=Mail&pass=3892
|
|
|
|
il n'y a que 2 différences : le «pseudo», et le «pass».
|
|
|
|
## Analyser un apache-status
|
|
|
|
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
|
|
~~~
|
|
|
|
# Processus / Process
|
|
|
|
## Surveiller les nouveaux processus créés
|
|
|
|
### Liste simple
|
|
|
|
~~~{.bash}
|
|
$ ps -e -o etimes=,pid,cmd | sort -rn | awk '{if ($1!=0 && $3!~/\[.*\]/) print $0 }'
|
|
~~~
|
|
|
|
### il y a moins de X minutes
|
|
|
|
~~~{.bash}
|
|
$ MIN=5; ps -e -o etimes=,pid,cmd | sort -rn | awk '{if ($1<'$(( MIN * 60 ))' && $1>0 && $3!~/\[.*\]/) print $0 }'
|
|
~~~
|
|
|
|
### Watch
|
|
|
|
Toutes les 5 secondes :
|
|
|
|
~~~{.bash}
|
|
$ watch -n 5 -d "ps -e -o etimes=,pid,user,cmd | sort -n | awk '{if (\$1==0 || \$2==$$ || \$3~/watch/ || \$3~/\[.*\]/) {} else print \$0 }'"
|
|
~~~
|
|
|
|
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"
|
|
~~~
|
|
|
|
## Lister avec le plus de fils (/fork)
|
|
|
|
~~~{.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
|
|
~~~
|
|
|
|
## Consommation Swap
|
|
|
|
~~~{.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
|
|
~~~
|
|
|
|
## Consommation RAM (VmSize)
|
|
|
|
~~~{.bash}
|
|
$ top -o RES
|
|
~~~
|
|
|
|
### Par utilisateur
|
|
|
|
~~~{.bash}
|
|
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
|
|
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
|
|
~~~
|
|
|
|
## Fichiers ouverts
|
|
|
|
### 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
|
|
~~~
|
|
|
|
### Selon l'utilisateur
|
|
|
|
~~~
|
|
# lsof -u UID
|
|
~~~
|
|
|
|
Si www-data a uid=33, lister fichiers ouvert par serveur-web :
|
|
|
|
~~~
|
|
# lsof -u 33 | awk '{ print $2 " = " $9 }' | grep "/home/.*$"
|
|
~~~
|
|
|
|
## 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 :
|
|
|
|
~~~
|
|
dd if=/dev/zero of=10M.bin bs=1024 count=0 seek=$[1024*100]
|
|
~~~
|
|
|
|
Pour 100Mio :
|
|
|
|
~~~
|
|
dd if=/dev/zero of=100M.bin bs=1024 count=0 seek=$[1024*1000]
|
|
~~~
|
|
|
|
Pour 1Gio :
|
|
|
|
~~~
|
|
dd if=/dev/zero of=1G.bin bs=1024 count=0 seek=$[1024*1024]
|
|
~~~
|
|
|
|
## stdout / stderr
|
|
|
|
Renvoyer stdout/stderr dans une même sortie (par exemple /dev/null) :
|
|
|
|
~~~
|
|
eject /dev/coin > /dev/null 2>&1
|
|
~~~
|
|
|
|
Détecter les tabulations
|
|
|
|
~~~
|
|
$ grep -P '\t'
|
|
~~~
|
|
|
|
# Serveur mail
|
|
|
|
## Avoir vision des différentes erreurs mailq (MAILER-DAEMON)
|
|
|
|
~~~{.bash}
|
|
(for id in $(mailq | grep MAILER\-DAEMON | cut -d' ' -f1); do postcat -q $id| grep Diagnostic\-Code\:; done) | sort | uniq -c | sort -n
|
|
~~~
|