Muitas vezes, quero obter o nome de login associado a um ID de usuário e, como é um caso de uso comum, decidi escrever uma função de shell para fazer isso. Enquanto eu uso principalmente distribuições GNU / Linux, tento escrever meus scripts para serem o mais portáveis possível e verificar se o que estou fazendo é compatível com POSIX.
Analisar /etc/passwd
A primeira abordagem que tentei foi analisar /etc/passwd
(usando awk
).
awk -v uid="$uid" -F: '$3 == uid {print $1}' /etc/passwd
No entanto, o problema dessa abordagem é que os logins podem não ser locais, por exemplo, a autenticação do usuário pode ser via NIS ou LDAP.
Use o getent
comando
O uso getent passwd
é mais portátil do que a análise, /etc/passwd
pois também consulta bancos de dados NIS ou LDAP não locais.
getent passwd "$uid" | cut -d: -f1
Infelizmente, o getent
utilitário não parece ser especificado pelo POSIX.
Use o id
comando
id
é o utilitário padronizado do POSIX para obter dados sobre a identidade de um usuário.
As implementações BSD e GNU aceitam um ID do usuário como um operando:
- id (1) - páginas de manual do OpenBSD
- id (1) - Página de manual do FreeBSD
- GNU Coreutils: chamada de identificação
Isso significa que ele pode ser usado para imprimir o nome de login associado a um ID do usuário:
id -nu "$uid"
No entanto, fornecer IDs do usuário como operando não é especificado no POSIX; descreve apenas o uso de um nome de login como o operando.
Combinando todas as opções acima
Eu considerei combinar as três abordagens acima em algo como o seguinte:
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
}
No entanto, isso é desajeitado, deselegante e - mais importante - não robusto; sai com um status diferente de zero se o ID do usuário for inválido ou não existir. Antes de escrever um script de shell mais longo e desajeitado que analise e armazene o status de saída de cada chamada de comando, pensei em perguntar aqui:
Existe uma maneira mais elegante e portátil (compatível com POSIX) de obter o nome de login associado a um ID do usuário?
getent
nemid
vai retornar nada passado o primeiro jogo; a única maneira de encontrar todos eles é enumerar todos os usuários, se o banco de dados do usuário permitir. (Procurando/etc/passwd
trabalhos para usuários definidos lá, obviamente)./etc/passwd
e/etc/shadow
para testar esse cenário e verifiquei que ambosid
egetent passwd
se comportam como você descreve. Se, em algum momento, eu acabar usando um sistema em que um usuário tem vários nomes, farei o mesmo que esses utilitários de sistema e simplesmente tratarei a primeira ocorrência como o nome canônico desse usuário.setuid(some_id)
, e não há requisitos quesome_id
possam fazer parte de qualquer banco de dados do usuário. Com coisas como namespaces de usuário no Linux, isso pode se tornar uma suposição incapacitante para seus scripts.getpwuid()
função quels
usa para converter UIDs em nomes de login. A resposta de Gilles é uma maneira mais direta e eficiente de conseguir isso.Respostas:
Uma maneira comum de fazer isso é testar se o programa que você deseja existe e está disponível no seu site
PATH
. Por exemplo:Como o POSIX
id
não suporta argumentos UID, aelif
cláusula forid
deve testar não apenas seid
está no PATH, mas também se será executado sem erros. Isso significa que ele pode ser executadoid
duas vezes, o que felizmente não terá um impacto perceptível no desempenho. Também é possível que ambosid
eawk
sejam executados, com o mesmo desempenho insignificante.BTW, com este método, não há necessidade de armazenar a saída. Apenas um deles será executado, portanto, apenas um imprimirá a saída para a função retornar.
fonte
if
parafi
dentro{ ... } | head -n 1
. ou seja, largue tudo, exceto a primeira partida do uid. mas isso significa que você precisará capturar o código de saída de qualquer programa executado.id
que não aceita um ID como operando, achei que testar seu status de saída poderia ser problemático - como saber a diferença entre um nome de login que não existe ou um UID que não existe. É possível que um nome de login seja composto apenas por caracteres numéricos: gnu.org/software/coreutils/manual/html_node/…if command -v getent >/dev/null;
vez de terif [ -x /usr/bin/getent ] ;
a chance de esses utilitários terem um caminho diferente.command -v
para esse fim: pubs.opengroup.org/onlinepubs/9699919799/utilities/command.html (embora eu só o tenha testado com odash
shell interno).type foo >/dev/null 2>/dev/null
trabalha em todos os aspectos que já vi.command
é relativamente moderno.Não há nada no POSIX que possa ajudar a não ser
id
. Tentarid
e voltar a analisar/etc/passwd
é provavelmente o mais portátil possível.O BusyBox
id
não aceita IDs de usuário, mas os sistemas com o BusyBox geralmente são sistemas embarcados autônomos, onde a análise/etc/passwd
é suficiente.Caso encontre um sistema não-GNU em
id
que não aceite IDs de usuário, você também pode tentar ligargetpwuid
via Perl, com a chance de estar disponível:Ou Python:
fonte
/etc/passwd
não é de todo portátil e não funcionará para back-end de arquivos que não sejam do passwd, como o LDAP.id
).POSIX especifica
getpwuid
como uma função C padrão para procurar no banco de dados do usuário por um ID do usuário, permitindo que o ID seja convertido em um nome de login. Eu baixei o código fonte do GNU coreutils e posso ver essa função sendo usada na implementação de utilitários comoid
els
.Como exercício de aprendizado, escrevi este programa C rápido e sujo para simplesmente atuar como um invólucro para essa função. Lembre-se de que não tenho programado em C desde a faculdade (há muitos anos) e não pretendo usá-lo na produção, mas pensei em publicá-lo aqui como uma prova de conceito (se alguém quiser editá-lo) , fique à vontade):
Não tive a chance de testá-lo com NIS / LDAP, mas notei que, se houver várias entradas para o mesmo usuário
/etc/passwd
, ele ignorará todas, exceto a primeira.Exemplo de uso:
fonte
Geralmente, eu recomendaria não fazer isso. O mapeamento de nomes de usuário para uids não é um para um, e as suposições de codificação que você pode converter de volta de um uid para obter um nome de usuário vão quebrar as coisas. Por exemplo, geralmente executo contêineres de espaço para nome de usuário completamente livres de raiz, fazendo com que os arquivos
passwd
egroup
no contêiner mapeiem todos os nomes de usuário e grupo para o ID 0; isso permite que a instalação de pacotes funcione semchown
falhar. Mas se algo tentar converter 0 de volta em um uid e não conseguir o que espera, ele será quebrado gratuitamente. Portanto, neste exemplo, em vez de converter novamente e comparar nomes de usuários, você deve converter em uids e comparar nesse espaço.Se você realmente precisar fazer essa operação, poderá ser possível executar de maneira semi-portável, se você for root, criando um arquivo temporário,
chown
inserindo-o no uid e usando-ols
para ler novamente e analisar o nome do proprietário. Mas eu usaria uma abordagem bem conhecida que não é padronizada, mas "portátil na prática", como uma das que você já encontrou.Mas, novamente, não faça isso. Às vezes, algo difícil de fazer é enviar uma mensagem.
fonte