Je souhaite souvent obtenir le nom de connexion associé à un ID utilisateur et comme il s'est avéré qu'il s'agit d'un cas d'utilisation courant, j'ai décidé d'écrire une fonction shell pour ce faire. Bien que j'utilise principalement des distributions GNU / Linux, j'essaie d'écrire mes scripts pour être aussi portables que possible et de vérifier que ce que je fais est compatible POSIX.
Analyser /etc/passwd
La première approche que j'ai essayée a été d'analyser /etc/passwd
(en utilisant awk
).
awk -v uid="$uid" -F: '$3 == uid {print $1}' /etc/passwd
Cependant, le problème avec cette approche est que les connexions peuvent ne pas être locales, par exemple, l'authentification des utilisateurs pourrait être via NIS ou LDAP.
Utilisez la getent
commande
L'utilisation getent passwd
est plus portable que l'analyse /etc/passwd
car elle interroge également les bases de données NIS ou LDAP non locales.
getent passwd "$uid" | cut -d: -f1
Malheureusement, l' getent
utilitaire ne semble pas être spécifié par POSIX.
Utilisez la id
commande
id
est l'utilitaire normalisé POSIX pour obtenir des données sur l'identité d'un utilisateur.
Les implémentations BSD et GNU acceptent un ID utilisateur comme opérande:
- id (1) - Pages de manuel OpenBSD
- id (1) - Page de manuel de FreeBSD
- GNU Coreutils: invocation d'un identifiant
Cela signifie qu'il peut être utilisé pour imprimer le nom de connexion associé à un ID utilisateur:
id -nu "$uid"
Cependant, fournir des ID utilisateur en tant qu'opérande n'est pas spécifié dans POSIX; il décrit uniquement l'utilisation d'un nom de connexion comme opérande.
Combiner tout ce qui précède
J'ai envisagé de combiner les trois approches ci-dessus en quelque chose comme ce qui suit:
get_username(){
uid="$1"
# First try using getent
getent passwd "$uid" | cut -d: -f1 ||
# Next try using the UID as an operand to id.
id -nu "$uid" ||
# As a last resort, parse `/etc/passwd`.
awk -v uid="$uid" -F: '$3 == uid {print $1}' /etc/passwd
}
Cependant, c'est maladroit, inélégant et - plus important encore - pas robuste; il se termine avec un état différent de zéro si l'ID utilisateur n'est pas valide ou n'existe pas. Avant d'écrire un script shell plus long et plus maladroit qui analyse et stocke l'état de sortie de chaque appel de commande, j'ai pensé demander ici:
Existe-t-il un moyen plus élégant et portable (compatible POSIX) d'obtenir le nom de connexion associé à un ID utilisateur?
getent
niid
ne retournera quoi que ce soit après la première correspondance; la seule façon de les trouver tous est d'énumérer tous les utilisateurs, si la base de données des utilisateurs le permet. (Recherche dans les/etc/passwd
œuvres pour les utilisateurs définis là, évidemment.)/etc/passwd
et/etc/shadow
de tester ce scénario et vérifié que les deuxid
etgetent passwd
se comportent comme vous le décrivez. Si, à un moment donné, je finis par utiliser un système où un utilisateur a plusieurs noms, je ferai la même chose que ces utilitaires système et traiterai simplement la première occurrence comme le nom canonique de cet utilisateur.setuid(some_id)
, et aucune exigence nesome_id
peut faire partie d'une base de données utilisateur. Avec des choses comme les espaces de noms d'utilisateurs sous Linux, cela peut devenir une hypothèse paralysante pour vos scripts.getpwuid()
fonction quils
utilise pour traduire les UID en noms de connexion. La réponse de Gilles est une manière plus directe et efficace d'y parvenir.Réponses:
Une façon courante de le faire est de tester si le programme souhaité existe et est disponible depuis votre
PATH
. Par exemple:Comme POSIX
id
ne prend pas en charge les arguments UID, laelif
clause forid
doit tester non seulement si elle seid
trouve dans le PATH, mais également si elle s'exécutera sans erreur. Cela signifie qu'il peut s'exécuterid
deux fois, ce qui, heureusement, n'aura pas d'impact notable sur les performances. Il est également possible que les deuxid
etawk
soient exécutés, avec les mêmes performances négligeables.BTW, avec cette méthode, il n'est pas nécessaire de stocker la sortie. Un seul d'entre eux sera exécuté, donc un seul imprimera la sortie pour la fonction à retourner.
la source
if
àfi
dans{ ... } | head -n 1
. c'est-à-dire supprimer tout sauf le premier match uid. mais cela signifie que vous devrez capturer le code de sortie de tout programme exécuté.id
qui n'accepte pas un ID comme opérande, j'ai pensé que tester son état de sortie pourrait être problématique - comment faire la différence entre un nom de connexion qui n'existe pas ou un UID qui n'existe pas. Il est possible qu'un nom de connexion ne soit composé que de caractères numériques: gnu.org/software/coreutils/manual/html_node/…if command -v getent >/dev/null;
au lieu deif [ -x /usr/bin/getent ] ;
la chance que ces utilitaires aient un chemin différent.command -v
à cette fin: pubs.opengroup.org/onlinepubs/9699919799/utilities/command.html (bien que je ne l'ai jamais testé qu'avec ledash
shell intégré).type foo >/dev/null 2>/dev/null
fonctionne sur tous les sh que j'ai jamais vus.command
est relativement moderne.Il n'y a rien dans POSIX qui pourrait aider à part ça
id
. Essayerid
et se replier sur l'analyse/etc/passwd
est probablement aussi portable que possible dans la pratique.BusyBox
id
n'accepte pas les ID utilisateur, mais les systèmes avec BusyBox sont généralement des systèmes intégrés autonomes où l'analyse/etc/passwd
est suffisante.Si vous rencontrez un système non GNU qui
id
n'accepte pas les ID utilisateur, vous pouvez également essayer d'appelergetpwuid
via Perl, au cas où il serait disponible:Ou Python:
la source
/etc/passwd
n'est pas du tout portable et ne fonctionnera pas pour les backends non-passwd-file comme LDAP.id
).POSIX spécifie en
getpwuid
tant que fonction C standard pour rechercher dans la base de données utilisateur un ID utilisateur permettant de traduire l'ID en un nom de connexion. J'ai téléchargé le code source de GNU coreutils et je peux voir que cette fonction est utilisée dans leur implémentation d'utilitaires tels queid
etls
.Comme exercice d'apprentissage, j'ai écrit ce programme C rapide et sale pour simplement agir comme un wrapper pour cette fonction. Gardez à l'esprit que je n'ai pas programmé en C depuis l'université (il y a de nombreuses années) et je n'ai pas l'intention de l'utiliser en production mais j'ai pensé que je le posterais ici comme preuve de concept (si quelqu'un veut le modifier) , N'hésitez pas):
Je n'ai pas eu la chance de le tester avec NIS / LDAP mais j'ai remarqué que s'il y a plusieurs entrées pour le même utilisateur
/etc/passwd
, il ignore tout sauf le premier.Exemple d'utilisation:
la source
En général, je déconseille de faire cela. Le mappage des noms d'utilisateur aux UID n'est pas un à un, et les hypothèses de codage que vous pouvez convertir à partir d' un UID pour obtenir un nom d'utilisateur vont casser les choses. Par exemple, j'exécute souvent des conteneurs d'espace de noms d'utilisateurs entièrement sans racine en faisant en sorte que les fichiers
passwd
etgroup
dans le conteneur mappent tous les noms d'utilisateurs et de groupes à l'id 0; cela permet d'installer des packages pour fonctionner sanschown
échec. Mais si quelque chose essaie de reconvertir 0 en uid et n'obtient pas ce qu'il attend, il se cassera gratuitement. Donc, dans cet exemple, au lieu de reconvertir et de comparer les noms d'utilisateur, vous devez convertir en uids et comparer dans cet espace.Si vous avez vraiment besoin de faire cette opération, il peut être possible de le faire de manière semi-portative si vous êtes root, en créant un fichier temporaire,
chown
en l'intégrant à l'UID, puis en utilisantls
pour lire et analyser le nom du propriétaire. Mais j'utiliserais simplement une approche bien connue qui n'est pas standardisée mais "portable dans la pratique", comme l'une de celles que vous avez déjà trouvées.Mais encore une fois, ne faites pas ça. Parfois, il est difficile de vous envoyer un message.
la source