Ich möchte oft den Anmeldenamen einer Benutzer-ID zuordnen. Da dies ein häufiger Anwendungsfall ist, habe ich mich dazu entschlossen, eine Shell-Funktion zu schreiben. Während ich hauptsächlich GNU / Linux-Distributionen verwende, versuche ich, meine Skripte so portabel wie möglich zu gestalten und zu überprüfen, ob meine Arbeit mit POSIX kompatibel ist.
Parse /etc/passwd
Der erste Ansatz, den ich versuchte, war das Parsen /etc/passwd
(Verwenden awk
).
awk -v uid="$uid" -F: '$3 == uid {print $1}' /etc/passwd
Das Problem bei diesem Ansatz ist jedoch, dass die Anmeldungen möglicherweise nicht lokal sind, z. B. kann die Benutzerauthentifizierung über NIS oder LDAP erfolgen.
Verwenden Sie den getent
Befehl
Die Verwendung getent passwd
ist portabler als das Parsen, /etc/passwd
da hierdurch auch nicht-lokale NIS- oder LDAP-Datenbanken abgefragt werden.
getent passwd "$uid" | cut -d: -f1
Leider getent
scheint das Dienstprogramm nicht von POSIX angegeben zu werden.
Verwenden Sie den id
Befehl
id
ist das POSIX-standardisierte Dienstprogramm zum Abrufen von Daten zur Identität eines Benutzers.
BSD- und GNU-Implementierungen akzeptieren eine Benutzer-ID als Operanden:
Das heißt, es kann verwendet werden, um den Anmeldenamen zu drucken, der einer Benutzer-ID zugeordnet ist:
id -nu "$uid"
Die Angabe von Benutzer-IDs als Operand ist in POSIX jedoch nicht angegeben. Es wird nur die Verwendung eines Anmeldenamens als Operand beschrieben.
Kombinieren Sie alle oben genannten
Ich überlegte, ob ich die drei oben genannten Ansätze in etwa wie folgt kombinieren sollte:
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
}
Dies ist jedoch klobig, unelegant und - was noch wichtiger ist - nicht robust; Es wird mit einem Status ungleich Null beendet, wenn die Benutzer-ID ungültig oder nicht vorhanden ist. Bevor ich ein längeres und komplexeres Shell-Skript schreibe, das den Beendigungsstatus jedes Befehlsaufrufs analysiert und speichert, dachte ich, ich würde hier fragen:
Gibt es eine elegantere und portablere (POSIX-kompatible) Möglichkeit, den Anmeldenamen einer Benutzer-ID zuzuordnen?
getent
noch zurückgegebenid
wird. Die einzige Möglichkeit, sie alle zu finden, besteht darin, alle Benutzer aufzulisten, sofern die Benutzerdatenbank dies zulässt. (In/etc/passwd
Werken nach dort definierten Benutzern suchen , offensichtlich.)/etc/passwd
und/etc/shadow
dieses Szenario getestet und überprüft, ob beideid
undgetent passwd
das von Ihnen beschriebene Verhalten zutreffen . Wenn ich irgendwann ein System verwende, auf dem ein Benutzer mehrere Namen hat, mache ich dasselbe wie diese Systemdienstprogramme und behandle einfach das erste Vorkommen als den kanonischen Namen für diesen Benutzer.setuid(some_id)
, und es gibt keine Anforderung,some_id
die Teil einer Benutzerdatenbank sein kann. Bei Benutzernamensräumen unter Linux kann dies zu einer lähmenden Annahme für Ihre Skripte werden.getpwuid()
Funktionls
zu sein, mit der UIDs in Anmeldenamen übersetzt werden. Gilles 'Antwort ist eine direktere und effizientere Möglichkeit, dies zu erreichen.Antworten:
Ein gängiger Weg, dies zu tun, besteht darin, zu testen, ob das gewünschte Programm existiert und von Ihrem verfügbar ist
PATH
. Beispielsweise:Da POSIX
id
keine UID-Argumente unterstützt, muss in derelif
Klausel fürid
nicht nur geprüft werden, obid
sich der Pfad befindet, sondern auch, ob er fehlerfrei ausgeführt wird. Dies bedeutet, dass es möglicherweiseid
zweimal ausgeführt wird, was sich glücklicherweise nicht merklich auf die Leistung auswirkt. Es ist auch möglich, dass beideid
und ausgeführtawk
werden, mit der gleichen vernachlässigbaren Leistungseinbuße.Übrigens muss bei dieser Methode die Ausgabe nicht gespeichert werden. Nur einer von ihnen wird ausgeführt, sodass nur einer die Ausgabe druckt, damit die Funktion zurückkehrt.
quelle
if
bisfi
in ein{ ... } | head -n 1
. dh alle bis auf das erste UID-Match löschen. Dies bedeutet jedoch, dass Sie den Exit-Code des ausgeführten Programms erfassen müssen.id
, die keine ID als Operanden akzeptiert, stellte ich fest, dass das Testen des Exit-Status problematisch sein kann - wie man den Unterschied zwischen einem nicht vorhandenen Anmeldenamen und einer UID erkennt existiert nicht. Es ist möglich, dass ein Anmeldename nur aus numerischen Zeichen besteht: gnu.org/software/coreutils/manual/html_node/…if command -v getent >/dev/null;
anstelle desif [ -x /usr/bin/getent ] ;
Zufalls verwenden, dass diese Dienstprogramme einen anderen Pfad haben.command -v
für diesen Zweck: pubs.opengroup.org/onlinepubs/9699919799/utilities/command.html (obwohl ich es nur mit derdash
eingebauten Shell getestet habe).type foo >/dev/null 2>/dev/null
arbeiten Sie an jedem Sh, den ich jemals gesehen habe.command
ist vergleichsweise modern.Es gibt nichts in POSIX, das anders als helfen würde
id
. Der Versuchid
, das Parsen wieder aufzunehmen,/etc/passwd
ist wahrscheinlich so portabel wie es in der Praxis üblich ist.BusyBox
id
akzeptiert keine Benutzer-IDs, aber Systeme mit BusyBox sind normalerweise autonome eingebettete Systeme, bei denen das Parsen/etc/passwd
ausreicht.Falls Sie auf ein Nicht-GNU-System stoßen , auf dem
id
Benutzer-IDs nicht akzeptiert werden, können Sie auch versuchen,getpwuid
über Perl anzurufen, sofern dies möglich ist:Oder Python:
quelle
/etc/passwd
ist überhaupt nicht portierbar und funktioniert nicht für Backends, die keine Passwd-Dateien enthalten, wie z. B. LDAP.id
).POSIX gibt
getpwuid
als Standard-C-Funktion an, dass die Benutzerdatenbank nach einer Benutzer-ID durchsucht werden soll, damit die ID in einen Anmeldenamen übersetzt werden kann. Ich habe den Quellcode für GNU coreutils heruntergeladen und kann sehen, dass diese Funktion bei der Implementierung von Dienstprogrammen wieid
und verwendet wirdls
.Als Lernübung habe ich dieses einfache C-Programm geschrieben, um einfach als Wrapper für diese Funktion zu fungieren. Denken Sie daran, dass ich seit dem College (vor vielen Jahren) nicht mehr in C programmiert habe und dies nicht in der Produktion verwenden möchte, aber ich dachte, ich würde es hier als Proof-of-Concept posten (wenn jemand es bearbeiten möchte) , fühlen Sie sich frei):
Ich hatte keine Möglichkeit, es mit NIS / LDAP zu testen, aber ich bemerkte, dass, wenn es mehrere Einträge für denselben Benutzer gibt
/etc/passwd
, alle außer dem ersten ignoriert werden.Anwendungsbeispiel:
quelle
Generell würde ich davon abraten. Die Zuordnung von Benutzernamen zu Benutzer-IDs ist keine Eins-zu-Eins-Zuordnung, und die Kodierung von Annahmen, die Sie von einer Benutzer-ID zurückkonvertieren können, um einen Benutzernamen zu erhalten , kann zu Problemen führen. Zum Beispiel führe ich häufig vollständig root-freie Benutzer-Namespace-Container aus, indem ich die
passwd
undgroup
-Dateien im Container dazu bringe, alle Benutzer- und Gruppennamen der ID 0 zuzuordnen. Auf diese Weise können Pakete problemlos installiert werdenchown
. Aber wenn etwas versucht, 0 zurück in eine UID umzuwandeln und nicht das bekommt, was es erwartet, wird es unbeabsichtigt kaputt gehen. In diesem Beispiel sollten Sie also, anstatt zurück zu konvertieren und Benutzernamen zu vergleichen, in UIDs konvertieren und in diesem Bereich vergleichen.Wenn Sie diesen Vorgang wirklich ausführen müssen, ist es unter Umständen möglich, einen semi-
chown
portablen Vorgang als Root auszuführen, indem Sie eine temporäre Datei erstellen, diese an die UID senden und dannls
den Namen des Besitzers mit lesen und analysieren. Aber ich würde nur einen bekannten Ansatz verwenden, der nicht standardisiert, sondern "in der Praxis portabel" ist, wie einer, den Sie bereits gefunden haben.Aber tu das nicht. Manchmal ist es schwierig, Ihnen eine Nachricht zu senden.
quelle