OVH Cloud OVH Cloud

Savoir, par un script non interactif, si une interface est wireless

16 réponses
Avatar
Francois Lafont
Bonjour à tous,

En fait, j'ai 2 questions.

1. Je me demandais si on pouvait savoir, par un script non interactif
et de manière _fiable_ (ou de la manière la plus fiable possible), si
une interface (au sens interface réseau du noyau) correspond à une
carte réseau Wifi ou non (et dans ce cas je considère que c'est une
carte réseau filaire). Le script prendrait en argument le nom d'une
des interfaces retournées par la commande "ip link" (par exemple) :

is_wireless.sh "eth1" && echo OK
is_wireless.sh "wlan0" && echo OK

Là, ce serait en shell mais n'importe quel langage de script mon
conviendrait (bon j'avoue j'aurais une préférence pour le shell mais
bon ;)).

Évidemment, si vous me dites qu'il suffit juste de tester que le nom
de l'interface commence par « wlan » et que c'est un test 100% fiable
alors Ok. Mais je constate qu'on peut tomber sur des noms bizarres
parfois. Par exemple, récemment suite à une installation « out-of-the-box »
d'une Ubuntu Xenial, je me suis retrouvé avec une interface nommée
enp0s3 au lieu du bon vieux eth0. Alors ok, là c'était de la carte
réseau filaire Ethernet mais du coup, le nom de l'interface est-il
vraiment une information fiable ?

J'aimerais que ce script soit compatible Debian Jessie et Ubuntu
Xenial si possible.

2. Par ailleurs, savez-vous s'il y a une option qui m'aurait échappée
pour parser facilement (typiquement parser dans un script) la sortie
des commandes « ip ». Par exemple, avec la commande « ip addr » j'ai
la liste de mes interfaces avec leur IP etc. Si je veux récupérer l'IP
de l'interface eth0 dans la sortie de « ip addr », évidemment je peux
le faire à coup de grep, awk etc. mais je trouve cela fragile. Par
exemple, il serait plus robuste de pouvoir parser une sortie json ou
je ne sais quoi. Je suis surpris de ne pas avoir trouvé d'option pour
ça. Mais j'ai peut-être mal cherché.

Merci d'avance pour votre aide.

--
François Lafont

6 réponses

1 2
Avatar
Benoit Izac
Bonjour,

Le 12/05/2016 à 03:00, Francois Lafont a écrit dans le message
<5733d591$0$9689$ :

Si c'est juste l'adresse MAC :
MAC=$(cat /sys/class/net/<ifname>/address)

Tu peux regarder également /sys/class/net/<ifname>/operstate pour savoir
si l'interface est up ou down ; voir
<https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-class-net>
pour d'autres informations disponibles.



Ok merci. Ce sont des manières de faire que je ne connaissais pas.
Personnellement je faisais ça :

mac=$(ip link show eth0 | awk '/link/ether/ {print $2}')

Mais du coup, est-ce que c'est mieux de passer par l'arborescence /sys/ ?
Par exemple, est-ce que je n'ai pas intérêt à rester sur du « haut » niveau
avec la commande "ip" plutôt que d'utiliser l'arbo /sys/ qui, peut-être,
pourra changer avec l'évolution du noyau Linux ? Je ne dis pas que c'est
le cas bien sûr, je me pose la question. Après la sortie de "ip" sera-t-elle
plus stable que l'arbo /sys/ pas sûr non plus...



Aucune idée, les deux ont à peu près le même âge ; soit un peu plus de
10 ans. Si tu veux vraiment du haut niveau, utilise Python avec
netifaces ou Perl avec Net::Interface (ou autre chose qui propose
l'équivalent).

Pour l'IP, il faut bricoler :
<http://unix.stackexchange.com/questions/119269/how-to-get-ip-address-using-shell-script>
Soit, par exemple, si tu n'as pas besoin de la longueur du préfixe :
ip addr | awk '/scope global/{split($2, a, "/"); print a[1]}'



Ok, au passage je découvre la fonction split() de awk.
Comme tu dis, faut bricoler un peu. Une option --json (ou n'importe quoi qui
puisse se parser facilement je me moque que ce soit du json en particulier)
aurait été commode je trouve. Et je constate que de telle option sont quand
même souvent présentes mais pas là.



Mais il te faut bien un langage de haut niveau pour analyser la sortie
json donc je ne vois pas trop l'intérêt. Pour ma part, je n'ai jamais
utilisé une commande qui me sort du json et, d'ailleurs, ça ne court pas
les rues :

% ls /usr/share/man/man8 | wc -l
1003
% for f in /usr/share/man/man8/*; do zgrep -il json "$f"; done | wc -l
11
% ls /usr/share/man/man1 | wc -l
3250
% for f in /usr/share/man/man1/*; do zgrep -il json "$f"; done | wc -l
23

Soit à peu près 0.8 % (je n'ai pas été vérifier si ce sont bien des
options).

--
Benoit Izac
Avatar
Francois Lafont
On 12/05/2016 19:37, Benoit Izac wrote:

Aucune idée, les deux ont à peu près le même âge ; soit un peu plus de
10 ans. Si tu veux vraiment du haut niveau, utilise Python avec
netifaces ou Perl avec Net::Interface (ou autre chose qui propose
l'équivalent).



Ok.

Mais il te faut bien un langage de haut niveau pour analyser la sortie
json donc je ne vois pas trop l'intérêt.



Encore une fois, json, csv, xml ou n'importe quoi d'autre, je m'en moque.
J'ai dit json parce que c'est un peu le truc à la mode en ce moment mais
je voulais simplement signifier par là une option pour pouvoir parser la
sortie facilement et de manière robuste dans un script (il semblait pourtant
avoir bien précisé que le json en soi je me moque).

Par exemple, dans le cas du json, tu as la commande jq qui te permet de
récupérer facilement une donnée. Mais si c'est du csv et bien je dirais
que awk fait parfaitement l'affaire etc. Ce que je veux dire par exemple,
c'est qu'il me semble que quelque chose comme :

ip addr show eth0 | awk '/scope global/{split($2, a, "/"); print a[1]}'

est plus fragile (et moins lisible aussi) qu'un truc (complètement fictif
donc) comme :

ip addr show eth0 --json | jq --raw-output '.["addresses"][0]'

Pour ma part, je n'ai jamais utilisé une commande qui me sort du json et,
d'ailleurs, ça ne court pas les rues :



Faut pas se focaliser sur json. ;)

% ls /usr/share/man/man8 | wc -l
1003
% for f in /usr/share/man/man8/*; do zgrep -il json "$f"; done | wc -l
11
% ls /usr/share/man/man1 | wc -l
3250
% for f in /usr/share/man/man1/*; do zgrep -il json "$f"; done | wc -l
23

Soit à peu près 0.8 % (je n'ai pas été vérifier si ce sont bien des
options).



Encore une fois, j'ai dit json comme j'aurais pu te dire csv, xml etc.
Tiens par exemple, je suis tombé sur la commande nmcli (issue du paquet
network-manager). Ça tombe bien, ça tombe pile dans le sujet du fil
pour savoir si une interface est wireless ou non :

~$ nmcli --field device,type,state dev status
DEVICE TYPE STATE
wlan0 802-11-wireless unavailable
eth1 802-3-ethernet connected
eth0 802-3-ethernet connected

Bon, ok, là pour le coup c'est pas dur à parser vu qu'on a déjà un csv en
fait. Mais les développeurs de cette commande ont été sympas, ils ont prévu
l'option --terse :

-t, --terse
Output is terse. This mode is designed and suitable for
computer (script) processing.

Ce qui donne :

~$ nmcli --terse --field device,type,state dev status
wlan0:802-11-wireless:unavailable
eth1:802-3-ethernet:connected
eth0:802-3-ethernet:connected

Je me trompe peut-être mais il me semble que ce n'est pas rare d'avoir
une commande qui structure sa sortie pour la rendre plus facilement parsable
dans un script. Après, je n'ai pas fait d'études statistiques sur la question. ;)

--
François Lafont
Avatar
Benoit Izac
Bonjour,

Le 12/05/2016 à 20:35, Francois Lafont a écrit dans le message
<5734ccfc$0$7119$ :

Mais il te faut bien un langage de haut niveau pour analyser la sortie
json donc je ne vois pas trop l'intérêt.



Encore une fois, json, csv, xml ou n'importe quoi d'autre, je m'en moque.
J'ai dit json parce que c'est un peu le truc à la mode en ce moment mais
je voulais simplement signifier par là une option pour pouvoir parser la
sortie facilement et de manière robuste dans un script (il semblait pourtant
avoir bien précisé que le json en soi je me moque).

Par exemple, dans le cas du json, tu as la commande jq qui te permet de
récupérer facilement une donnée. Mais si c'est du csv et bien je dirais
que awk fait parfaitement l'affaire etc.



Détrompe-toi, il y a tellement de format dit CSV (ou plutôt DSV) :
séparateur « t », « , » pour les anglophone, « ; » pour la France car
« , » sert de séparateur décimal ; on peut vouloir échapper le
séparateur avec un «  » ou entourer un champ avec des « '' » ou des
« "" » pour mettre le séparateur dedans.

Bref, ce n'est pas toujours simple et ça peut surtout partir en vrille
sévère si l'entrée diffère légèrement un beau jour. Il en va de même
pour XML qui nécessite une bibliothèque (un module dans un langage plus
au niveau) pour que ça fonctionne correctement et surtout que tu n'aies
pas à te prendre la tête car d'autre l'on fait pour toi.

Ce que je veux dire par exemple, c'est qu'il me semble que quelque
chose comme :

ip addr show eth0 | awk '/scope global/{split($2, a, "/"); print a[1]}'

est plus fragile (et moins lisible aussi) qu'un truc (complètement fictif
donc) comme :

ip addr show eth0 --json | jq --raw-output '.["addresses"][0]'



Je préférerais un truc comme :
ip addr show dev eth0 -o address # renvoie l'adresse IP
ip link show dev eth0 -o address # renvoie l'adresse MAC
Comme ça il n'y a pas à bricoler derrière.

Tiens par exemple, je suis tombé sur la commande nmcli (issue du paquet
network-manager). Ça tombe bien, ça tombe pile dans le sujet du fil
pour savoir si une interface est wireless ou non :

~$ nmcli --field device,type,state dev status
DEVICE TYPE STATE
wlan0 802-11-wireless unavailable
eth1 802-3-ethernet connected
eth0 802-3-ethernet connected

Bon, ok, là pour le coup c'est pas dur à parser vu qu'on a déjà un csv en
fait. Mais les développeurs de cette commande ont été sympas, ils ont prévu
l'option --terse :

-t, --terse
Output is terse. This mode is designed and suitable for
computer (script) processing.

Ce qui donne :

~$ nmcli --terse --field device,type,state dev status
wlan0:802-11-wireless:unavailable
eth1:802-3-ethernet:connected
eth0:802-3-ethernet:connected



Mauvais exemple. Imagine que je fasse de l'IP aliasing et que, par
conséquent, mes interfaces soient nommées « eth0 », « eth0:0 »,
« eth0:1 » ; il y a de forte chance que le script qui découpe sur « : »
se vautre lamentablement lorsqu'il va vouloir récupérer le type.

Je me trompe peut-être mais il me semble que ce n'est pas rare d'avoir
une commande qui structure sa sortie pour la rendre plus facilement parsable
dans un script.



C'est possible. J'ai surtout en tête l'option « -o » de ps(1) qui permet
effectivement d'extraire facilement les données que l'on souhaite.

--
Benoit Izac
Avatar
Francois Lafont
On 12/05/2016 22:33, Benoit Izac wrote:

Détrompe-toi, il y a tellement de format dit CSV (ou plutôt DSV) :
séparateur « t », « , » pour les anglophone, « ; » pour la France car
« , » sert de séparateur décimal ; on peut vouloir échapper le
séparateur avec un « » ou entourer un champ avec des « '' » ou des
« "" » pour mettre le séparateur dedans.

Bref, ce n'est pas toujours simple et ça peut surtout partir en vrille
sévère si l'entrée diffère légèrement un beau jour. Il en va de même
pour XML qui nécessite une bibliothèque (un module dans un langage plus
au niveau) pour que ça fonctionne correctement et surtout que tu n'aies
pas à te prendre la tête car d'autre l'on fait pour toi.



J'avoue ne pas connaître les spécifications exactes du csv. Pour moi, c'est
une sortie où il y a un caractère délimiteur et celui-ci ne se retrouve nulle
part ailleurs sauf au niveau des délimitations. Sorti de ce cadre, je
n'utilise pas. Mais j'imagine bien que ça doit être affreux comme tu le décris. ;)

Je préférerais un truc comme :
ip addr show dev eth0 -o address # renvoie l'adresse IP
ip link show dev eth0 -o address # renvoie l'adresse MAC



Ah oui, bien sûr, ce serait encore mieux.

Comme ça il n'y a pas à bricoler derrière.



Voilà, il y a du bricolage ensuite mais je trouve que le bricolage est
plus sûr quand il se base sur une sortie dont la structure est bien connue
(et dont il existe déjà des programmes pour la parser) que lorsqu'il faut
faire des petites regex ici ou là basée sur des motifs supposés constants
que j'ai constatés moi-même à travers quelques exemples de sortie.

~$ nmcli --field device,type,state dev status
DEVICE TYPE STATE
wlan0 802-11-wireless unavailable
eth1 802-3-ethernet connected
eth0 802-3-ethernet connected

Bon, ok, là pour le coup c'est pas dur à parser vu qu'on a déjà un csv en
fait. Mais les développeurs de cette commande ont été sympas, ils ont prévu
l'option --terse :

-t, --terse
Output is terse. This mode is designed and suitable for
computer (script) processing.

Ce qui donne :

~$ nmcli --terse --field device,type,state dev status
wlan0:802-11-wireless:unavailable
eth1:802-3-ethernet:connected
eth0:802-3-ethernet:connected



Mauvais exemple. Imagine que je fasse de l'IP aliasing et que, par
conséquent, mes interfaces soient nommées « eth0 », « eth0:0 »,
« eth0:1 » ; il y a de forte chance que le script qui découpe sur « : »
se vautre lamentablement lorsqu'il va vouloir récupérer le type.



C'était juste un exemple de commande qui montrait un « effort »
(pas forcément efficace à 100%) fait pour fournir une option rendant
la sortie parsable facilement. Au passage, les aliases ne sont pas
énumérés dans la sortie sur ma Debian Wheezy (mais bon c'est un détail
ici).

~$ ip addr show eth1
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 00:14:d1:2f:6c:ac brd ff:ff:ff:ff:ff:ff
inet 10.0.0.2/8 brd 10.255.255.255 scope global eth1
inet 10.0.0.13/8 brd 10.255.255.255 scope global secondary eth1:0
inet6 fe80::214:d1ff:fe2f:6cac/64 scope link
valid_lft forever preferred_lft forever

~$ nmcli --terse --field device,type,state dev status
wlan0:802-11-wireless:unavailable
eth1:802-3-ethernet:connected
eth0:802-3-ethernet:connected

Je me trompe peut-être mais il me semble que ce n'est pas rare d'avoir
une commande qui structure sa sortie pour la rendre plus facilement parsable
dans un script.



C'est possible. J'ai surtout en tête l'option « -o » de ps(1) qui permet
effectivement d'extraire facilement les données que l'on souhaite.



Oui, avec -o de ps, on peut pas mal se faciliter la tâche dans les scripts.
Un autre exemple qui me vient en tête, c'est haproxy qui possède une page
web où on peut superviser son état mais il y a aussi une url accessible sans
authentification (qui marche en local uniquement) et qui permet d'avoir plus
ou moins les même infos que la page web mais sous un format type csv assez
facile à « greper ».

Bref, merci de ton aide Benoît.
À+

--
François Lafont
Avatar
Pascal Hambourg
Francois Lafont a écrit :
On 12/05/2016 22:33, Benoit Izac wrote:

Mauvais exemple. Imagine que je fasse de l'IP aliasing et que, par
conséquent, mes interfaces soient nommées « eth0 », « eth0:0 »,
« eth0:1 » ; il y a de forte chance que le script qui découpe sur « : »
se vautre lamentablement lorsqu'il va vouloir récupérer le type.



C'était juste un exemple de commande qui montrait un « effort »
(pas forcément efficace à 100%) fait pour fournir une option rendant
la sortie parsable facilement. Au passage, les aliases ne sont pas
énumérés dans la sortie sur ma Debian Wheezy (mais bon c'est un détail
ici).



Les alias IP ne sont pas des interfaces mais des labels. Il n'y a guère
que l'antique ifconfig qui les considère comme de vraies interfaces.
Avatar
Benoit Izac
Bonjour,

Le 14/05/2016 à 10:09, Pascal Hambourg a écrit dans le message
<nh6mev$1q8i$ :

Mauvais exemple. Imagine que je fasse de l'IP aliasing et que, par
conséquent, mes interfaces soient nommées « eth0 », « eth0:0 »,
« eth0:1 » ; il y a de forte chance que le script qui découpe sur « : »
se vautre lamentablement lorsqu'il va vouloir récupérer le type.



C'était juste un exemple de commande qui montrait un « effort »
(pas forcément efficace à 100%) fait pour fournir une option rendant
la sortie parsable facilement. Au passage, les aliases ne sont pas
énumérés dans la sortie sur ma Debian Wheezy (mais bon c'est un détail
ici).



Les alias IP ne sont pas des interfaces mais des labels. Il n'y a guère
que l'antique ifconfig qui les considère comme de vraies interfaces.



Effectivement, mais je suis resté à l'antiquité. D'ailleurs, netifaces
de Python ansi que Net::Interface de Perl considèrent également les
aliases comme des interfaces.

--
Benoit Izac
1 2