Script avec boucle, éviter les doublons de variable

Salut,
Pas sur que mon titre soit très clair.

Suite à mes histoire de blocage de client torrent, j’essaye de faire un script.

[code]#!/bin/sh
HOSTN=127.0.0.1

lsof -Pwlni | grep “:56969 (EST*” | sed -e “s|[->:]| |g” | awk ‘{print $11}’ | while read PORT; do
echo ${PORT}

tcpkill -i eth0 ${port}

done

lsof -Pwlni | grep “:56969 (EST*” | sed -e “s|[->:]| |g” | awk ‘{print $12}’ | while read IPADDR; do
echo ${IPADDR}

iptables -t filter -I INPUT -s ${IPADDR} -j REJECT

done

exit 0
[/code]

Pour l’instant je ne fait qu’un echo des résultats.
J’obtiens bien la liste des ports et des IP, mais… Les IP sont répétées, j’aimerais empêcher les doublons dans mes boucles.

Il faudrait faire une seconde boucle, mais…
Sauriez vous comment faire ?

Bonjour,

As tu esssayé de mettre un sort -u après le awk ?

C’est en effet ce qui me semble être le meilleur cas.

Je n’ai pas pu résister au plaisir de diminuer le nombre de pipe :

Par contre j’aurais tendance à vouloir ne faire qu’une seul boucle :

for ligne in $(lsof -Pwlni | sed -n 's/.* TCP \(.*\):\(.*\)->.*3128 (EST.*/ip=\1;port=\2/p') ; do eval "${ligne}" echo "ip -> ${ip} et port -> ${port}" done

Si cette dernière te pose des problèmes lié au fait que tu lance deux fois tcpkill ou iptable avec les même paramètres, tu peut stocker les ip et les ports dans des listes :

[code]for ligne in $(lsof -Pwlni | sed -n ‘s/.* TCP (.):(.)->.3128 (EST./ip=\1;port=\2/p’) ; do
eval "${ligne}"
ips=( $ips $ip ); ports=( $ports ${port})
done

for port in $(printf “%s\n” “${ports[@]}” | sort -u) ; do
tcpkill -i eth0 "${port}"
done

for ipaddr in $(printf “%s\n” “${ips[@]}” | sort -u) ; do
iptables -t filter -I INPUT -s “${ipaddr}” -j REJECT
done[/code]

Ça paraît plus lourd mais ça permet de n’avoir qu’un seul appel à lsof et d’avoir une cohérence entre les ip et les ports.

Salut,
Merci pour vos réponses. Coucou Michel :006

Je teste ça lundi sur la machine ou j’ai le script !
Restera l’histoire de tcpkill qui a un fonctionnement que je ne comprends pas bien encore. :confused:

Salut,
Ça semble parfait, merci!

Voici ce que ça donne:

[code]#!/bin/bash

for ligne in $(lsof -Pwlni | sed -n ‘s/.* TCP (.):(.)->.5222 (EST./ip=\1;port=\2/p’) ; do
eval "${ligne}"
ips=($ips $ip); ports=($ports ${port})
done

for port in $(printf “%s\n” “${ports[@]}” | sort -u) ; do
tcpkill -i eth0 port ${port} &
sleep 2
/usr/bin/killall tcpkill
done

for ipaddr in $(printf “%s\n” “${ips[@]}” | sort -u) ; do
iptables -t filter -I INPUT -s “${ipaddr}” -j REJECT
done

exit 0[/code]

J’ai compris que tcpkill ne s’arrête pas tant qu’on ne lui dit pas. Il faut donc insérer un sleep et “tuer” le processus avant de reprendre la boucle.

Merci, c’est résolu!

[quote=“lol”]for port in $(printf "%s\n" "${ports[@]}" | sort -u) ; do tcpkill -i eth0 port ${port} & sleep 2 /usr/bin/killall tcpkill done

J’ai compris que tcpkill ne s’arrête pas tant qu’on ne lui dit pas. Il faut donc insérer un sleep et “tuer” le processus avant de reprendre la boucle.[/quote]
C’est lourd quand même :

for port in $(printf "%s\n" "${ports[@]}" | sort -u) ; do tcpkill -i eth0 port ${port} & done sleep 2 /usr/bin/killall tcpkill
C’est n*2s fois plus rapide (n étant le nombre de connexion que tu souhaite tuer). Mais tu tue tout les tcpkill si tu veut être un peu moins brutal :

for port in $(printf "%s\n" "${ports[@]}" | sort -u) ; do tcpkill -i eth0 port ${port} & tcpkillpid=( ${tcpkillpid} $! ) done sleep 2 /usr/bin/killall "${tcpkillpid[@]}"
Ne tueras que ceux que tu as lancé.

Au passage je viens de relire un bout du man de bash, il y a des trucs marrant à faire comme ça pour la fin de ton script :

for ipaddr in $(printf "%s\n" "${ips[@]}" | sort -u) ; do iptables -t filter -I INPUT -s "${ipaddr}" -j REJECT & done wait

Ça lance tout tes iptables en parallèle et le wait attend qu’ils soient tous fini pour continuer (le parallélisme en bash est très avancé tout en étant simple je trouve).