19
0
Fork 0
wiki/TipsShell.md

9.0 KiB

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) :

/etc/alternatives/

Sinon en ligne de commande :

$ EDITOR=nano vipw

Manipulations

Tâches de fond

$ 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"
$ 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 :

$ ./liste_serveur.sh | tac | grep -n NOUVEAU_SERVEUR
2:NOUVEAU_SERVEUR

Fichiers et FS

Ordinaire

Savoir si lignes en doublon dans un fichier

$ uniq -d <fichier>

ou autrement (appliquer un filtre différent) :

$ diff <fichier> <(cat <fichier> | uniq)

Comparer deux fichiers quant à l'existence de nouvelles lignes

$ grep -F -x -v -f <fichier1> <fichier2>

Supprimer des vieux fichiers

  • Par exemple, si + vieux de 30 jours en modification :
$ find DIR/ -type f -mtime +30 -delete
$ find DIR/ -type f -mtime +30 -exec rm '{}' \;

Comparer deux fichiers à travers SSH

$ diff <fichier> <(ssh REMOTE cat <fichier>)

Répertoire

Surveiller les ouvertures/écriture des fichiers présent dans un répertoire

$ iwatch <target>

Savoir les différents users qui ont écris dans /tmp

$ 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 :

$ 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

Pour certaines anciennes versions :

# ncdu --exclude "/home/*" /

Sinon voir du côté de HowtoDUC.

Tester l'écriture disque

Simplement, en écriture (fichier de 5.1GB) :

$ dd if=/dev/zero of=test count=10000000

Lister les répertoires ayant le plus de fichiers <=> max inode

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

$ for user in $(getent passwd | awk -F ':' '$3 > 10000 {printf $1 " "}'); do groups $user; done

LDAP

$ 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

$ date; (timeout 60 tail -f access.log | cut -d' ' -f1) | sort | uniq -c | sort -n

En direct

  • Version simple :
$ tail -f access.log | stdbuf -oL cut -d ' ' -f1 | uniq -c 
  • Version couleur :
$ 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.

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 :

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».

Processus / Process

Surveiller les nouveaux créés

Liste simple

$ ps -e -o etimes=,pid,cmd | sort -rn | awk '{if ($1!=0 && $3!~/\[.*\]/)  print $0 }'

Watch

Toutes les 5 secondes :

$ 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) :

$ 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)

(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

(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

Fichiers ouvert

socket/port

  • Connaître les sockets ouvertes et ports en écoutent par un processus :
$ lsof -Pan -p PID -i
  • Connaître le pid qui écoute sur un port (2ème colonne) :
$ 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/.*$"