POSIX उपयोगकर्ता आईडी के साथ जुड़े उपयोगकर्ता नाम पाने के लिए संगत तरीका है


23

मैं अक्सर एक उपयोगकर्ता आईडी से जुड़ा लॉगिन नाम प्राप्त करना चाहता हूं और क्योंकि यह एक सामान्य उपयोग मामला साबित होता है, इसलिए मैंने ऐसा करने के लिए एक शेल फ़ंक्शन लिखने का फैसला किया। जब मैं मुख्य रूप से जीएनयू / लिनक्स वितरण का उपयोग करता हूं, तो मैं अपनी स्क्रिप्ट को यथासंभव पोर्टेबल होने की कोशिश करता हूं और यह जांचने के लिए कि मैं क्या कर रहा हूं, पॉसिक्स-संगत है।

पार्स /etc/passwd

मैंने जो पहली कोशिश की वह पार्स /etc/passwd(उपयोग करना awk) थी।

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

हालांकि, इस दृष्टिकोण के साथ समस्या यह है कि लॉगिन स्थानीय नहीं हो सकते हैं, उदाहरण के लिए, उपयोगकर्ता प्रमाणीकरण एनआईएस या एलडीएपी के माध्यम से हो सकता है।

getentकमांड का उपयोग करें

उपयोग getent passwdकरने से पार्स करने से अधिक पोर्टेबल है /etc/passwdक्योंकि यह गैर-स्थानीय एनआईएस या एलडीएपी डेटाबेस पर भी सवाल उठाता है।

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

दुर्भाग्य से, getentउपयोगिता POSIX द्वारा निर्दिष्ट नहीं लगती है।

idकमांड का उपयोग करें

id उपयोगकर्ता की पहचान के बारे में डेटा प्राप्त करने के लिए POSIX- मानकीकृत उपयोगिता है।

BSD और GNU कार्यान्वयन एक उपयोगकर्ता आईडी को एक ऑपरेंड के रूप में स्वीकार करते हैं:

इसका अर्थ है कि इसका उपयोग उपयोगकर्ता आईडी से जुड़े लॉगिन नाम को प्रिंट करने के लिए किया जा सकता है:

id -nu "$uid"

हालाँकि, उपयोगकर्ता आईडी प्रदान करना क्योंकि संचालन POSIX में निर्दिष्ट नहीं है; यह केवल ऑपरेंड के रूप में एक लॉगिन नाम के उपयोग का वर्णन करता है ।

उपरोक्त सभी को मिलाकर

मैंने उपरोक्त तीन दृष्टिकोणों को निम्नलिखित की तरह संयोजित करने पर विचार किया:

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
}

हालांकि, यह क्लंकी है, असभ्य और - अधिक महत्वपूर्ण बात - मजबूत नहीं; यदि उपयोगकर्ता आईडी अमान्य है या मौजूद नहीं है, तो यह एक गैर-शून्य स्थिति से बाहर निकलता है। इससे पहले कि मैं एक लंबी और क्लंकियर शेल स्क्रिप्ट लिखूं जो प्रत्येक कमांड आह्वान के निकास की स्थिति का विश्लेषण और संग्रह करता है, मैंने सोचा कि मैं यहां पूछूंगा:

क्या उपयोगकर्ता आईडी से लॉगिन नाम प्राप्त करने का एक और अधिक सुरुचिपूर्ण और पोर्टेबल (POSIX- संगत) तरीका है?


10
अतिरिक्त मनोरंजन के लिए, विचार करें कि कई उपयोगकर्ता नाम एक ही आईडी पर मैप कर सकते हैं ...
स्टीफन किट

इस सवाल के संदर्भ में एक ही आईडी से मैप किया गया एकाधिक उपयोगकर्ता नाम के साथ इस मुद्दे है कि न तो है getentऔर न ही idपहला मैच पिछले कुछ भी प्राप्त होगा; उन सभी को खोजने का एकमात्र तरीका सभी उपयोगकर्ताओं को एन्यूमरेट करना है, यदि उपयोगकर्ता डेटाबेस इसकी अनुमति देता है। ( /etc/passwdवहाँ परिभाषित उपयोगकर्ताओं के लिए काम करता है, स्पष्ट रूप से देख रहे हैं।)
स्टीफन किट

1
धन्यवाद @StephenKitt मैंने अपनी /etc/passwdऔर /etc/shadowइस परिदृश्य का परीक्षण करने के लिए ऐसी प्रविष्टि बनाई और सत्यापित किया कि दोनों idऔर getent passwdजैसा आप वर्णन करते हैं वैसा ही व्यवहार करते हैं। यदि, किसी स्तर पर, मैं एक ऐसी प्रणाली का उपयोग कर रहा हूं, जहां एक उपयोगकर्ता के कई नाम हैं, तो मैं इन सिस्टम उपयोगिताओं के समान ही करूंगा और बस उस उपयोगकर्ता के लिए विहित नाम के रूप में पहली घटना का इलाज करूंगा।
एंथनी जी -

1
क्या POSIX एक प्रयोक्ता आईडी की आवश्यकता होती है एक उपयोगकर्ता नाम के साथ जुड़ा होना बिल्कुल भी ? रूट के रूप में चलने वाला कोई भी कार्यक्रम कॉल कर सकता है setuid(some_id), और इसकी कोई आवश्यकता नहीं है जो some_idकिसी भी उपयोगकर्ता डेटाबेस का हिस्सा हो सकता है। लिनक्स पर उपयोगकर्ता नामस्थान जैसी चीजों के साथ यह आपकी लिपियों के लिए एक अपंग धारणा हो सकती है।
मॉसवी

1
@ फ़ीलिपोस जो getpwuid()फ़ंक्शन को कॉल करने के एक महंगे तरीके की तरह लगता है जो lsलॉगिन नामों के लिए यूआईडी का अनुवाद करने के लिए उपयोग करता है। गिल्स का जवाब इसे पूरा करने का एक अधिक प्रत्यक्ष और कुशल तरीका है।
एंथनी जी -

जवाबों:


14

ऐसा करने का एक सामान्य तरीका यह है कि यदि आप जिस प्रोग्राम को चाहते हैं, वह मौजूद है और आपके पास उपलब्ध है या नहीं PATH। उदाहरण के लिए:

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 idUID तर्कों का समर्थन नहीं करता है, इसके लिए elifक्लॉज को idकेवल idPATH में है या नहीं , यह भी जांचना होगा कि यह बिना किसी त्रुटि के चलेगा या नहीं। इसका मतलब है कि यह idदो बार चल सकता है , जो सौभाग्य से प्रदर्शन पर ध्यान देने योग्य प्रभाव नहीं होगा। यह भी संभव है कि दोनों idऔर awkचलाए जाएंगे, उसी नगण्य प्रदर्शन के साथ।

BTW, इस पद्धति के साथ, आउटपुट को स्टोर करने की कोई आवश्यकता नहीं है। उनमें से केवल एक ही चलाया जाएगा, इसलिए फ़ंक्शन को वापस करने के लिए केवल एक आउटपुट प्रिंट करेगा।


एक ही uid होने एकाधिक उपयोगकर्ता नाम की संभावना से निपटने के लिए, से सब कुछ लपेटो ifकरने fiमें { ... } | head -n 1। यानी सभी को छोड़ दें लेकिन पहला यूआईडी मैच है। लेकिन इसका मतलब यह है कि आपको जो भी प्रोग्राम मिला है उसके एक्जिट कोड पर कब्जा करना होगा।
कैस

जवाब के लिए धन्यवाद। मैं उम्मीद कर रहा था कि कुछ अन्य उपयोगिता हो सकती है जो मैं भर में नहीं आया था लेकिन यह मददगार है। क्योंकि मेरे पास इसके क्रियान्वयन तक पहुंच नहीं है, इसलिए idएक आईडी को एक ऑपरेंड के रूप में स्वीकार नहीं करता, मुझे लगा कि इसकी निकास स्थिति का परीक्षण करना समस्याग्रस्त हो सकता है - एक लॉगिन नाम के बीच अंतर कैसे बताएं जो मौजूद नहीं है या यूआईडी नहीं है मौजूद नहीं है। केवल संख्यात्मक पात्रों से मिलकर एक लॉगिन नाम संभव है: gnu.org/software/coreutils/manual/html_node/…
एंथनी जी - मोनिका

1
प्रत्येक संपादन के साथ, फ़ंक्शन अधिक मजबूत होता जा रहा है। :) उस नोट पर, मैं शायद ऑफ-ऑन के if command -v getent >/dev/null;बजाय इसका उपयोग करूंगा if [ -x /usr/bin/getent ] ;कि इन उपयोगिताओं का एक अलग रास्ता है।
एंथोनी जी -

3
हाँ। मैं नियमित रूप से command -vइस उद्देश्य के लिए उपयोग करता हूं : pubs.opengroup.org/onlinepubs/9699919799/utilities/command.html (हालांकि मैंने केवल dashशेल शेल के साथ इसका कभी परीक्षण किया है )।
एंथनी जी -

1
@AnthonyGeoghegan यदि आपको कभी प्राचीन सिस्टम पर काम करना है, तो type foo >/dev/null 2>/dev/nullहर उस शोज़ पर काम करता है जो मैंने कभी देखा है। commandतुलनात्मक रूप से आधुनिक है।
गाइल्स 'एसओ- बुराई को रोकें'

6

POSIX में ऐसा कुछ भी नहीं है जो इसके अलावा अन्य मदद करे id। कोशिश कर रहा है idऔर पार्स करने पर वापस गिरने /etc/passwdके रूप में यह व्यवहार में हो जाता है शायद के रूप में पोर्टेबल है।

बिजीबॉक्स idयूजर आईडी स्वीकार नहीं करता है, लेकिन बिजीबॉक्स वाले सिस्टम आमतौर पर स्वायत्त एम्बेडेड सिस्टम हैं जहां पार्सिंग /etc/passwdपर्याप्त है।

यदि आप एक गैर-जीएनयू प्रणाली का सामना idकरते हैं, जहां उपयोगकर्ता आईडी स्वीकार नहीं करते हैं, तो आप getpwuidपर्ल के माध्यम से कॉल करने का प्रयास कर सकते हैं , जिस अवसर पर यह उपलब्ध है:

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

या अजगर:

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

2
पार्सिंग /etc/passwdबिल्कुल भी पोर्टेबल नहीं है, और यह एलडीएपी जैसी गैर-पासवार्ड-फाइल बैकएंड के लिए काम नहीं करेगा।
आर ..

मुझे वह पसंद है, मैं इसे चुरा लूंगा
cas

1
@ आर .. पूछने वाले को इस बात की जानकारी है, यह जवाब अन्यथा दावा नहीं करता है, तो आपकी टिप्पणी का क्या मतलब है?
गिल्स एसओ- बुराई को रोकें

इस उत्तर के लिए धन्यवाद। मैं आश्वस्त हूं कि कुछ अन्य उपयोगिता नहीं है, जिनके बारे में मुझे जानकारी नहीं थी। ऐसा लगता है कि POSIX UID को लॉगिन नाम में अनुवाद करने के लिए एक मानक C फ़ंक्शन को निर्दिष्ट करता है, लेकिन जरूरी नहीं कि वह एक संगत कमांड (इसके अलावा id) हो।
एंथनी जी -

2
अंतिम गिरावट के रूप में, जांच लें कि क्या सिस्टम पर एसी कंपाइलर है, तो एक आपूर्ति किए गए गेटप्विड () रैपर को संकलित करें ...
12:15

5

POSIX getpwuidएक मानक सी फ़ंक्शन के रूप में निर्दिष्ट करता है जो एक उपयोगकर्ता आईडी के लिए उपयोगकर्ता डेटाबेस को खोजता है जिससे आईडी को लॉगिन नाम में अनुवाद करने की अनुमति मिलती है। मैं जीएनयू के लिए स्रोत कोड डाउनलोड किया coreutils और इस समारोह जैसे उपयोगिताओं के उनके कार्यान्वयन में इस्तेमाल किया जा रहा देख सकते हैं idऔर ls

सीखने की कवायद के रूप में, मैंने इस फंक्शन के लिए एक आवरण के रूप में कार्य करने के लिए यह त्वरित और गंदा सी प्रोग्राम लिखा। यह ध्यान में रखें कि मैंने कॉलेज से (कई साल पहले) सी में प्रोग्राम नहीं किया है और मैं इसे प्रोडक्शन में इस्तेमाल करने का इरादा नहीं रखता लेकिन मुझे लगा कि मैं इसे कॉन्सेप्ट के सबूत के तौर पर पोस्ट करूंगा (अगर कोई इसे एडिट करना चाहता है तो) , स्वतंत्र महसूस करना):

#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 के साथ परीक्षण करने का मौका नहीं था, लेकिन मैंने देखा कि यदि एक ही उपयोगकर्ता के लिए कई प्रविष्टियाँ हैं /etc/passwd, तो यह सभी को अनदेखा करता है, लेकिन पहले।

उदाहरण का उपयोग:

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

$ ./get_user 99
Invalid user ID: 99

3

आम तौर पर, मैं ऐसा करने के खिलाफ सिफारिश करूंगा। उपयोगकर्ता नामों से uids के लिए मैपिंग एक-से-एक नहीं है, और एन्कोडिंग मान्यताओं है कि आप एक उपयोगकर्ता नाम पाने के लिए यूआईडी से वापस परिवर्तित कर सकते हैं चीजों को तोड़ने के लिए जा रहा है। उदाहरण के लिए, मैं अक्सर पूरी तरह से रूट-फ्री यूजर-नेमस्पेस कंटेनरों को चलाता हूं passwdऔर groupकंटेनर मैप में फाइलों और आईडी 0 के सभी उपयोगकर्ता और समूह के नाम; यह बिना किसी chownविफलता के काम करने के लिए संकुल को स्थापित करने की अनुमति देता है । लेकिन अगर कुछ 0 को वापस यूआईडी में बदलने की कोशिश करता है और उसे उम्मीद नहीं है कि वह क्या करेगा, तो यह बहुत हद तक टूट जाएगा। इसलिए इस उदाहरण में, वापस परिवर्तित करने और उपयोगकर्ता नाम की तुलना करने के बजाय, आपको uids में परिवर्तित होना चाहिए और उस स्थान की तुलना करनी चाहिए।

यदि आपको वास्तव में इस ऑपरेशन को करने की आवश्यकता है, तो संभव है कि यदि आप रूट कर रहे हैं, तो सेमी-पोर्टेबली करना संभव है, एक अस्थायी फ़ाइल बनाकर, chownइसे यूआईडी पर lsभेज दें , फिर वापस पढ़ने और मालिक के नाम को पार्स करने के लिए उपयोग करें। लेकिन मैं सिर्फ एक प्रसिद्ध दृष्टिकोण का उपयोग करूंगा जो कि मानकीकृत नहीं है, लेकिन "पोर्टेबल में अभ्यास", जैसे कि आप पहले से ही पाए गए लोगों में से एक हैं।

लेकिन फिर, यह मत करो। कभी-कभी कुछ करना कठिन होता है, जो आपको संदेश भेज रहा है।


1
टिप्पणियाँ शुद्ध। मैं दोनों प्रतिभागियों को याद दिलाना चाहता हूं कि टिप्पणियों को सिविल होना चाहिए।
terdon
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.