A menudo quiero obtener el nombre de inicio de sesión asociado con una ID de usuario y, debido a que se ha demostrado que es un caso de uso común, decidí escribir una función de shell para hacer esto. Si bien uso principalmente distribuciones GNU / Linux, trato de escribir mis scripts para que sean lo más portátiles posible y verificar que lo que estoy haciendo sea compatible con POSIX.
Analizar gramaticalmente /etc/passwd
El primer enfoque que intenté fue analizar /etc/passwd
(usando awk
).
awk -v uid="$uid" -F: '$3 == uid {print $1}' /etc/passwd
Sin embargo, el problema con este enfoque es que los inicios de sesión pueden no ser locales, por ejemplo, la autenticación del usuario podría ser a través de NIS o LDAP.
Usa el getent
comando
El uso getent passwd
es más portátil que el análisis, /etc/passwd
ya que también consulta bases de datos NIS o LDAP no locales.
getent passwd "$uid" | cut -d: -f1
Desafortunadamente, la getent
utilidad no parece estar especificada por POSIX.
Usa el id
comando
id
es la utilidad estandarizada POSIX para obtener datos sobre la identidad de un usuario.
Las implementaciones BSD y GNU aceptan una ID de usuario como un operando:
- id (1) - páginas del manual de OpenBSD
- id (1) - Página del manual de FreeBSD
- GNU Coreutils: invocación de id
Esto significa que se puede usar para imprimir el nombre de inicio de sesión asociado con una ID de usuario:
id -nu "$uid"
Sin embargo, proporcionar ID de usuario como el operando no se especifica en POSIX; solo describe el uso de un nombre de inicio de sesión como operando.
Combinando todo lo anterior
Pensé en combinar los tres enfoques anteriores en algo como lo siguiente:
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
}
Sin embargo, esto es torpe, poco elegante y, lo que es más importante, no es robusto; sale con un estado distinto de cero si la ID de usuario no es válida o no existe. Antes de escribir un script de shell más largo y más grueso que analiza y almacena el estado de salida de cada invocación de comando, pensé en preguntar aquí:
¿Existe una forma más elegante y portátil (compatible con POSIX) de obtener el nombre de inicio de sesión asociado con una ID de usuario?
getent
niid
devolverá nada después de la primera coincidencia; la única forma de encontrarlos todos es enumerar a todos los usuarios, si la base de datos de usuarios lo permite. (Buscando/etc/passwd
obras para usuarios definidos allí, obviamente.)/etc/passwd
y/etc/shadow
para probar este escenario y verifiqué ambas cosasid
ygetent passwd
como usted describe. Si, en algún momento, termino usando un sistema en el que un usuario tiene varios nombres, haré lo mismo que estas utilidades del sistema y simplemente trataré la primera aparición como el nombre canónico para ese usuario.setuid(some_id)
, y no hay ningún requisito quesome_id
pueda ser parte de una base de datos de usuarios. Con cosas como los espacios de nombres de usuario en Linux, esto puede convertirse en una suposición paralizante para sus scripts.getpwuid()
función que sels
utiliza para traducir UID a nombres de inicio de sesión. La respuesta de Gilles es una forma más directa y eficiente de lograr esto.Respuestas:
Una forma común de hacer esto es probar si el programa que desea existe y está disponible en su
PATH
. Por ejemplo:Debido a que POSIX
id
no admite argumentos de UID, laelif
cláusulaid
tiene que probar no solo siid
está en la RUTA, sino también si se ejecutará sin error. Esto significa que puede ejecutarseid
dos veces, lo que afortunadamente no tendrá un impacto notable en el rendimiento. También es posible que tantoid
yawk
se ejecutará, con la misma insignificante impacto en el rendimiento.Por cierto, con este método, no hay necesidad de almacenar la salida. Solo se ejecutará uno de ellos, por lo que solo uno imprimirá la salida para que la función regrese.
fuente
if
afi
adentro{ ... } | head -n 1
. es decir, soltar todo menos el primer partido uid. pero eso significará que tendrá que capturar el código de salida de cualquier programa que se ejecute.id
que no acepte un ID como operando, pensé que probar su estado de salida podría ser problemático: cómo distinguir entre un nombre de inicio de sesión que no existe o un UID que no existe Es posible que un nombre de inicio de sesión consista solo en caracteres numéricos: gnu.org/software/coreutils/manual/html_node/…if command -v getent >/dev/null;
lugar deif [ -x /usr/bin/getent ] ;
la posibilidad de que estas utilidades tengan una ruta diferente.command -v
para este propósito: pubs.opengroup.org/onlinepubs/9699919799/utilities/command.html (aunque solo lo he probado con eldash
shell incorporado).type foo >/dev/null 2>/dev/null
trabaja en todas las cosas que he visto.command
Es comparativamente moderno.No hay nada en POSIX que pueda ayudar más que
id
. Intentarid
y volver a analizar/etc/passwd
es probablemente tan portátil como se pone en práctica.BusyBox
id
no acepta ID de usuario, pero los sistemas con BusyBox suelen ser sistemas integrados autónomos donde el análisis/etc/passwd
es suficiente.En caso de que encuentre un sistema que
id
no sea GNU y no acepte ID de usuario, también puede intentar llamargetpwuid
través de Perl, en caso de que esté disponible:O Python:
fuente
/etc/passwd
no es portátil en absoluto, y no funcionará para backends que no sean archivos de contraseña como LDAP.id
).POSIX especifica
getpwuid
como una función C estándar para buscar en la base de datos de usuario una ID de usuario que permita que la ID se traduzca a un nombre de inicio de sesión. Descargué el código fuente de GNU coreutils y puedo ver esta función siendo utilizada en su implementación de utilidades comoid
yls
.Como ejercicio de aprendizaje, escribí este programa de C rápido y sucio para simplemente actuar como una envoltura para esta función. Tenga en cuenta que no he programado en C desde la universidad (hace muchos años) y no tengo la intención de usar esto en la producción, pero pensé en publicarlo aquí como prueba de concepto (si alguien quiere editarlo) , sentirse libre):
No tuve la oportunidad de probarlo con NIS / LDAP, pero noté que si hay varias entradas para el mismo usuario
/etc/passwd
, ignora todas menos la primera.Ejemplo de uso:
fuente
En general, recomendaría no hacer esto. El mapeo de los nombres de usuario a los uid no es uno a uno, y los supuestos de codificación que puede convertir de un uid para obtener un nombre de usuario van a romper las cosas. Por ejemplo, a menudo ejecuto contenedores de espacio de nombres de usuario completamente libres de raíz haciendo que
passwd
ygroup
archivos en el contenedor asignen todos los nombres de usuarios y grupos al id 0; Esto permite instalar paquetes para que funcionen sinchown
fallar. Pero si algo intenta convertir 0 de nuevo a uid y no obtiene lo que espera, se romperá de forma gratuita. Entonces, en este ejemplo, en lugar de volver a convertir y comparar nombres de usuario, debe convertir a uids y comparar en ese espacio.Si realmente necesita hacer esta operación, podría ser posible hacerlo de forma semi-portátil si es root, haciendo un archivo temporal, llevándolo
chown
al uid y luego usandols
para volver a leer y analizar el nombre del propietario. Pero simplemente usaría un enfoque bien conocido que no está estandarizado sino que es "portátil en la práctica", como uno de los que ya encontró.Pero de nuevo, no hagas esto. A veces, algo difícil de hacer es enviarte un mensaje.
fuente