ユーザーIDに関連付けられたログイン名を取得したいことがよくありますが、これは一般的なユースケースであることが証明されているため、これを行うためのシェル関数を作成することにしました。私は主にGNU/Linuxディストリビューションを使用していますが、できるだけ移植性が高くなるようにスクリプトを作成し、自分がやっていることがPOSIX互換であることを確認しようとしています。
/etc/passwd
私が試した最初のアプローチは、/etc/passwd
を解析することでした(awk
を使用)。
awk -v uid="$uid" -F: '$3 == uid {print $1}' /etc/passwd
ただし、このアプローチの問題は、ログインがローカルでない可能性があることです。たとえば、ユーザー認証がNISまたはLDAPを介して行われる可能性があります。
getent
コマンドを使用するgetent passwd
を使用すると、/etc/passwd
を解析するよりも移植性が高くなります。これは、ローカル以外のNISまたはLDAPデータベースに対してもクエリを実行するためです。
getent passwd "$uid" | cut -d: -f1
残念ながら、getent
ユーティリティはPOSIXで指定されていないようです。
id
コマンドを使用するid
は、ユーザーのIDに関するデータを取得するためのPOSIX標準のユーティリティです。
BSDおよびGNUの実装は、オペランドとしてユーザーIDを受け入れます。
これは、ユーザーIDに関連付けられたログイン名を出力するために使用できることを意味します。
id -nu "$uid"
ただし、オペランドとしてユーザーIDを指定することは、POSIXでは指定されていません。 ログイン名をオペランドとして使用することについてのみ説明しています。
上記の3つのアプローチを次のようなものに組み合わせることを検討しました。
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
}
ただし、これは不格好で洗練されておらず、さらに重要なことに堅牢ではありません。ユーザーIDが無効であるか存在しない場合は、ゼロ以外のステータスで終了します。各コマンド呼び出しの終了ステータスを分析して保存する、より長くて不格好なシェルスクリプトを書く前に、私はここで質問したいと思いました。
ユーザーIDに関連付けられたログイン名を取得する、よりエレガントで移植可能な(POSIX互換)方法はありますか?
これを行う一般的な方法の1つは、必要なプログラムが存在し、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 id
はUID引数をサポートしていないため、Elif
のid
句は、id
がPATHにあるかどうかだけでなく、エラーなしで実行されます。これは、id
が2回実行される可能性があることを意味します。 id
とawk
の両方が実行され、パフォーマンスへの影響はほとんど無視できる可能性もあります。
ところで、この方法では、出力を保存する必要はありません。そのうちの1つだけが実行されるため、関数が返す出力を出力するのは1つだけです。
POSIXにはid
以外に役立つものはありません。 id
を試し、解析にフォールバック/etc/passwd
は、実際のところ、おそらく移植可能です。
BusyBoxのid
はユーザーIDを受け入れませんが、BusyBoxを備えたシステムは通常、/etc/passwd
で十分です。
GNU以外のシステムでid
がユーザーIDを受け付けない場合は、Perlで getpwuid
を呼び出してみてください。 :
username=$(Perl -e 'print((getpwuid($ARGV[0]))[0])) 2>/dev/null
if [ -n "$username" ]; then echo "$username"; return; fi
またはPython:
if python -c 'import pwd, sys; print(pwd.getpwuid(int(sys.argv[1]))).pw_name' 2>/dev/null; then return; fi
POSIXは getpwuid
を標準C関数として指定して、ユーザーデータベースでユーザーIDを検索し、IDをログイン名に変換できるようにしています。 GNUcoreutilsのソースコードをダウンロードして、id
などのユーティリティの実装でこの関数が使用されているのを確認できますおよびls
。
学習課題として、この関数のラッパーとして機能するように、この汚くて汚いCプログラムを作成しました。私は大学で(何年も前に)Cでプログラミングしていないので、これを本番環境で使用するつもりはありませんが、概念実証として(ここで編集したい場合は)ここに投稿したいと思いました。 、お気軽に):
#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
一般に、これを行わないことをお勧めします。ユーザー名からuidへのマッピングは1対1ではなく、ユーザー名を取得するためにback from uidを変換できるというエンコーディングの仮定は、問題を引き起こします。たとえば、コンテナー内のpasswd
およびgroup
ファイルをすべてのユーザー名とグループ名をID 0にマップすることで、完全にルートフリーのユーザー名前空間コンテナーを実行することがよくあります。これにより、chown
が失敗することなく、パッケージのインストールが機能するようになります。しかし、何かが0をuidに変換しようとして、期待した結果が得られない場合、無理に壊れてしまいます。したがって、この例では、ユーザー名を元に戻して比較する代わりに、uidに変換してそのスペースで比較する必要があります。
本当にこの操作を実行する必要がある場合、一時ファイルを作成し、それをuidにchown
ingし、次にls
を使用することにより、rootの場合は、移植性が高くなる可能性があります。所有者の名前を読み取って解析します。しかし、私は、あなたがすでに見つけたもののような、標準化されていないが「実際に移植可能」であるよく知られたアプローチを使用するだけです。
ただし、これを行わないでください。時には、あなたにメッセージを送るのが難しいことがある。