Bir kullanıcı kimliğiyle ilişkilendirilmiş kullanıcı adı almak için POSIX uyumlu yol


23

Genellikle bir kullanıcı kimliğiyle ilişkilendirilmiş giriş adını almak istiyorum ve bunun yaygın bir kullanım durumu olduğu kanıtlandığından, bunun için bir kabuk işlevi yazmaya karar verdim. Öncelikle GNU / Linux dağıtımlarını kullanırken, senaryolarımı olabildiğince taşınabilir olması için senaryoları yazmaya ve POSIX uyumlu olup olmadıklarını kontrol etmeye çalışıyorum.

Ayrıştırma /etc/passwd

Denedim ilk yaklaşım ayrıştırma /etc/passwd(kullanarak awk) oldu.

awk -v uid="$uid" -F: '$3 == uid {print $1}' /etc/passwd

Bununla birlikte, bu yaklaşımla ilgili sorun, girişlerin yerel olmayabilir, örneğin kullanıcı kimlik doğrulaması NIS veya LDAP yoluyla olabilir.

getentKomutu kullan

Yerel olmayan NIS veya LDAP veritabanlarını da getent passwdsorguladığından /etc/passwd, kullanmak ayrıştırmaya göre daha taşınabilir .

getent passwd "$uid" | cut -d: -f1

Ne yazık ki, getentyardımcı program POSIX tarafından belirtilmiş gibi görünmüyor.

idKomutu kullan

id Bir kullanıcının kimliği hakkında veri almak için POSIX standardize edilmiş bir yardımcı programdır.

BSD ve GNU uygulamaları bir kullanıcı kimliğini operand olarak kabul eder:

Bu, bir Kullanıcı Kimliği ile ilişkili giriş adını yazdırmak için kullanılabileceği anlamına gelir:

id -nu "$uid"

Bununla birlikte, kullanıcı kimliklerini işlenen olarak sağlamak POSIX'te belirtilmemiş; sadece bir işleç olarak giriş adının kullanılmasını açıklar .

Yukarıdakilerin hepsini birleştirmek

Yukarıdaki üç yaklaşımı aşağıdaki gibi bir şeye birleştirmeyi düşündüm:

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
}

Ancak, bu tıknaz, inelegant ve - daha önemlisi - sağlam değil; Kullanıcı Kimliği geçersizse veya yoksa sıfır olmayan bir durumla çıkar. Her komut çağrısının çıkış durumunu analiz eden ve depolayan daha uzun ve clunkier bir kabuk betiği yazmadan önce, burada soracağımı düşündüm:

Bir kullanıcı kimliğiyle ilişkili giriş adını almanın daha şık ve taşınabilir (POSIX uyumlu) bir yolu var mı?


10
Daha fazla eğlence için, birden fazla kullanıcı adının aynı kimliğe
eşlenebileceğini

Bu sorunun bağlamında aynı kimliğe eşleştirilmiş birden kullanıcı adlarıyla sorunu ne olduğunu getentne de idilk maçında geçen şey döndürür; hepsini bulmanın tek yolu, kullanıcı veritabanına izin veriyorsa, tüm kullanıcıları sıralamaktır. ( /etc/passwdAçıkçası, orada tanımlanan kullanıcılar için çalışmalara bakmak .)
Stephen Kitt

1
Teşekkür @StephenKitt yaşıyorum benim, böyle bir giriş yaratmış /etc/passwdve /etc/shadowbu senaryoyu test etmek ve hem doğruladıktan idve getent passwdaçıkladığınız olarak davranır. Bir aşamada, bir kullanıcının birden fazla isme sahip olduğu bir sistem kullanırsam, bu sistem yardımcı programlarıyla aynı şeyi yapacağım ve ilk oluşumu yalnızca o kullanıcı için geçerli olan ad olarak kabul edeceğim.
Anthony G - Monica için adalet

1
Bir kullanıcı adıyla ilişkili olduğu mu POSIX bir kullanıcı kimliği gerektiren hiç ? Kök olarak çalışan herhangi bir program arayabilir setuid(some_id)ve some_idherhangi bir kullanıcı veritabanının parçası olabilecek bir gereklilik yoktur . Linux'ta kullanıcı ad alanları gibi şeylerle, bu, betikleriniz için sakatlayıcı bir varsayım olabilir.
mosvy

1
@Philippos, kullanıcı adlarını kullanıcı adlarına çevirmek için kullanılan getpwuid()işlevi çağırmanın pahalı bir yolu gibi görünüyor ls. Gilles'un cevabı , bunu başarmanın daha doğrudan ve etkili bir yoludur.
Anthony G - Monica

Yanıtlar:


14

Bunu yapmanın yaygın yollarından biri, istediğiniz programın mevcut olup olmadığını test etmek PATH. Örneğin:

get_username(){
  uid="$1"

  # First try using getent
  if command -v getent > /dev/null 2>&1; then 
    getent passwd "$uid" | cut -d: -f1

  # Next try using the UID as an operand to id.
  elif command -v id > /dev/null 2>&1 && \
       id -nu "$uid" > /dev/null 2>&1; then
    id -nu "$uid"

  # Next try perl - perl's getpwuid just calls the system's C library getpwuid
  elif command -v perl >/dev/null 2>&1; then
    perl -e '@u=getpwuid($ARGV[0]);
             if ($u[0]) {print $u[0]} else {exit 2}' "$uid"

  # As a last resort, parse `/etc/passwd`.
  else
      awk -v uid="$uid" -F: '
         BEGIN {ec=2};
         $3 == uid {print $1; ec=0; exit 0};
         END {exit ec}' /etc/passwd
  fi
}

POSIX id, UID argümanlarını desteklemediğinden, elifyan tümce idyalnızca idPATH'de olup olmadığını değil, hatasız çalışıp çalışmayacağını test etmek zorundadır . Bu, idiki kez çalışabileceği anlamına gelir; bu, neyse ki performans üzerinde belirgin bir etkisi olmayacaktır. Hem olması da mümkündür idve awkaynı ihmal edilebilir performans isabet ile, işletilecek.

BTW, bu yöntemle, çıkışı depolamaya gerek yoktur. Bunlardan sadece biri çalıştırılacak, bu nedenle fonksiyonun geri dönmesi için yalnızca biri çıktı yazdıracak.


Aynı uid sahip birden fazla kullanıcı adları olasılığı ile başa çıkmak için, her şeyi sarmak ifiçin fide { ... } | head -n 1. yani ilk kullanıcı eşleşmesinden başka hepsini bırak. ancak bu, hangi programın çalıştırıldığının çıkış kodunu yakalamanız gerektiği anlamına gelir.
cas,

Cevap için teşekkürler. Karşılaşmadığım başka bir yardımcı program olabileceğini umuyordum, ancak bu yardımcı oldu. idBir kimliği işlenen olarak kabul etmeyen bir uygulamasına erişimim olmadığı için , çıkış durumunu test etmenin sorunlu olabileceğini düşündüm - var olmayan bir kullanıcı adı ya da bir kullanıcı kimliği arasındaki farkın nasıl söylendiğini yok Bir giriş adının yalnızca sayısal karakterlerden oluşması mümkündür: gnu.org/software/coreutils/manual/html_node/…
Anthony G - Monica için adalet

1
Her düzenleme ile, işlev daha sağlam hale geliyor. :) Bu kayda göre, muhtemelen bu araçların farklı bir yolu olması ihtimaline karşı kullanmak if command -v getent >/dev/null;yerine kullanırım if [ -x /usr/bin/getent ] ;.
Anthony G - Monica için adalet

3
Evet. command -vBu amaçla düzenli olarak kullanıyorum : pubs.opengroup.org/onlinepubs/9699919799/utilities/command.html (yalnızca dashkabuk yerleşik yapıyla test etsem de ).
Anthony G - Monica için adalet

1
@AnthonyGeoghegan Eski sistemler üzerinde çalışmak zorundaysanız, type foo >/dev/null 2>/dev/nullgördüğüm her sh üzerinde çalışır. commandnispeten modern.
Gilles 'SO- kötü davranmayı'

6

POSIX’de bunun dışında yardımcı olabilecek hiçbir şey yoktur id. idAyrıştırmaya geri dönüp denemek /etc/passwdmuhtemelen pratikte olduğu kadar taşınabilir.

BusyBox idKullanıcı kimliklerini kabul etmemektedir, ancak BusyBox bulunan sistemler genellikle ayrıştırmanın /etc/passwdyeterli olduğu özerk yerleşik sistemlerdir .

idKullanıcı kimliklerini kabul etmeyen GNU dışı bir sistemle karşılaşmanız durumunda getpwuid, mevcut olma ihtimali üzerine Perl yoluyla da arama yapabilirsiniz :

username=$(perl -e 'print((getpwuid($ARGV[0]))[0])) 2>/dev/null
if [ -n "$username" ]; then echo "$username"; return; fi

Veya Python:

if python -c 'import pwd, sys; print(pwd.getpwuid(int(sys.argv[1]))).pw_name' 2>/dev/null; then return; fi

2
Ayrıştırma /etc/passwdhiç taşınabilir değildir ve LDAP gibi passwd dosyası olmayan arka uçlar için çalışmaz.
R. ..

Bunu sevdim, çalacağım
cas

1
@R .. Asker bunun farkında değil, bu cevabın aksini iddia etmediğini, yorumunuzun amacı ne?
Gilles 'SO- kötülük yapmayı bırak'

Bu cevap için teşekkürler. Farkında olmadığım başka bir yardımcı program olmadığından eminim. POSIX, UID'yi bir oturum açma adına çevirmek için standart bir C işlevini belirtir ancak zorunlu olarak karşılık gelen bir komutu (dışında id) belirtmez .
Anthony G - Monica

2
Son bir geri dönüş olarak, sistemde ac derleyici olup olmadığını kontrol edin, sonra verilen bir getpwuid () sargısını derleyin ...
rackandboneman

5

POSIX getpwuid, kimliğin bir oturum açma adına çevrilmesine izin veren bir kullanıcı kimliği için kullanıcı veritabanında arama yapmak için standart bir C işlevi olduğunu belirtir . GNU coreutils'in kaynak kodunu indirdim ve bu fonksiyonun idve gibi uygulamaların uygulanmasında kullanıldığını görebiliyorum ls.

Bir öğrenme alıştırması olarak, bu hızlı ve kirli C programını, bu işlev için yalnızca bir sarmalayıcı olarak çalışmak üzere yazdım. Kolejden beri C programında programlanmadığımı (yıllar önce) ve bunu prodüksiyonda kullanmayı düşünmemiştim ama buraya bir kavram kanıtı olarak göndereceğimi düşündüm (biri düzenlemek isterse) , çekinmeyin):

#include <stdio.h>
#include <stdlib.h>  /* atoi */
#include <pwd.h>

int main( int argc, char *argv[] ) {
    uid_t uid;
    if ( argc >= 2 ) {
        /* NB: atoi returns 0 (super-user ID) if argument is not a number) */
        uid = atoi(argv[1]);
    }
    /* Ignore any other arguments after the first one. */
    else {
        fprintf(stderr, "One numeric argument must be supplied.\n");
        return 1;
    }

    struct passwd *pwd;
    pwd = getpwuid(uid);
    if (pwd) {
        printf("The login name for %d is: %s\n", uid, pwd->pw_name);
        return 0;
    }
    else {
        fprintf(stderr, "Invalid user ID: %d\n", uid);
        return 1;
    }
}

NIS / LDAP ile test etme şansım olmadı, ancak aynı kullanıcı için birden fazla giriş varsa /etc/passwd, hepsinin ilkini görmezden geldiğini fark ettim .

Örnek kullanım:

$ ./get_user ""
The login name for 0 is: root

$ ./get_user 99
Invalid user ID: 99

3

Genel olarak, bunu yapmamayı tavsiye ederim. Kullanıcı adlarından kullanıcı adlarına doğru eşleştirme birebir değildir ve bir kullanıcı adını almak için bir kullanıcı adından geri döndürebileceğiniz kodlama varsayımları bir şeyleri kıracaktır. Örneğin, çoğu zaman tamamen root-free kullanıcı-isim kapsayıcılarını çalıştırıp, konteyner içindeki passwdve groupdosyalarını tüm kullanıcı ve grup adlarını id 0; bu, paketleri kurmadan chownbaşarısız çalışmasına izin verir . Fakat eğer bir şey 0'ı tekrar bir üid'e dönüştürmeye çalışır ve beklediği şeyi almazsa, keyfi bir şekilde kıracaktır. Dolayısıyla bu örnekte, geri dönüşüm ve kullanıcı adlarını karşılaştırmak yerine, kullanıcı adlarına dönüştürmeniz ve bu alanda karşılaştırmanız gerekir.

Bu işlemi gerçekten yapmanız gerekiyorsa, kökseniz, geçici bir dosya chownoluşturarak, kullanıcı arabirimine girerek , daha sonra lssahibinin adını okumak ve ayrıştırmak için kullanıyorsanız, kökündeyseniz yarı taşınabilir yapmak mümkündür . Ancak, standartlaştırılmış, ancak pratikte taşınabilir olan, zaten bulduğunuzlardan biri gibi, iyi bilinen bir yaklaşım kullanırdım.

Ama yine, bunu yapma. Bazen yapmak zor olan bir şey sana mesaj yollamaktır.


1
Yorum temizlendi. Her iki katılımcıya da yorumların medeni olması gerektiğini hatırlatmak isterim .
Terdon
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.