シェルスクリプトですべてのユーザーを繰り返す方法は?
システムクリーンアップを実行するためのシェルスクリプトを作成しています。スクリプトがrootとして実行されたら、すべてのユーザーのホームディレクトリで要求されたタスクを実行したいと思います。
私はそれを繰り返すことができると思いました/etc/passwd
。しかし、そこには実際のユーザーよりも多くの項目があります。仮想ユーザーを削除する特別な方法はありますか/etc/passwd
?
私は(移植性のために)Bourne Shell用のカスタムソリューションを好みます。擬似コードと簡単な説明も歓迎します。
答え1
既存のユーザーアカウントをすべて列挙する標準コマンドはありません。ほとんどのUnix亜種では/etc/passwd
常に同じ既存の形式(コロンで区切られた列)のローカルアカウントのリストが含まれています。 Glibc がある Linux (つまり、組み込まれていない Linux) では、次のものを使用できます。getent
コマンド:getent passwd
に似ていますcat /etc/passwd
が、リモートアカウント(NIS、LDAPなど)も含めます。
次のコードスニペットはユーザーアカウントを列挙します。
getent passwd | while IFS=: read -r name password uid gid gecos home shell; do
echo "$name's home directory is $home"
done
ユーザーデータベースには、そのユーザーが血肉であるかどうかを示す情報がないため、完全に信頼できる方法で血育ユーザーをフィルタリングすることは不可能です。 (また:テストアカウントが含まれていますか?ゲストアカウントが含まれていますか?など)適用できるいくつかの経験的な方法は次のとおりです。
ホームディレクトリがシステムディレクトリとして表示されないユーザーをフィルタリングできます。
top=${home#/}; top=${top%%/*} case $top in |bin|dev|etc|lib*|no*|proc|sbin|usr|var) echo "Looks like a system user";; *) echo "Looks like a user with a real home directory";; esac
ユーザーが自分のホームディレクトリを所有しているかどうかをテストできます。これは、システムユーザーではなく実際のユーザーの場合はほとんど常に当てはまりますが、一部のシステムユーザーは自分のホームディレクトリを持っているため、あまり信頼できません。また、通常、そのディレクトリは自動的にマウントされますが、実際のユーザーのホームディレクトリは現在利用できない場合は存在しない可能性があります。 Linuxの場合:
if [ -d "$home" ] && [ "$(stat -c %u "$home")" = "$uid" ]; then echo "Likely flesh-and-blood" else echo "Probably a system user" fi
ユーザーが実際のシェルを持っているかどうかをテストできます。ただし、多くのシステムユーザーが同じ操作を実行するため、ホームディレクトリよりも信頼性がはるかに低くなります。
アカウントにパスワードがあるかどうかをテストできます。実際のユーザーが常にパスワードを持っているわけではないので、これは完全に信頼できません(たとえば、SSHキーしか持っていない可能性があります)。システムアカウントにパスワードがある場合もあります。特にルートアカウントにパスワードがあることがよくあります。これを行う方法はUnixのバリエーションによって異なり、通常はrootアクセスが必要です。シャドウパスワード(Linuxでは一般的)があるLinuxでは、
/etc/shadow
パスワードを見つける必要があります。case $(getent shadow "$name" | awk -F: '{print $2}') in ?|??) echo "No password on this account";; |\$*) echo "This account has a password";; *) echo "Weird password field";; esac
多くのシステムでは、システムアカウントと実際のユーザーのUID範囲を定義しています。これらの範囲はシステムによって異なります。デフォルトはUnixバリアントとディストリビューションによって異なり、通常はシステム管理者が設定できます。ほとんどのLinuxディストリビューションでは、スコープは次に定義されています。
login.defs
:人間ユーザーUID_MIN
の場合、UID_MAX
他の値はシステムアカウントのものです。
私はホームディレクトリへのパスが最も信頼できる単一の指標だと思いますが、それを他の経験的な方法と組み合わせることもできます。
答え2
質問に対するコメントといくつかの実験に基づいて、私が思いついた結果は次のとおりです。
#!/bin/bash
#Use awk to do the heavy lifting.
#For lines with UID>=1000 (field 3) grab the home directory (field 6)
usrInfo=$(awk -F: '{if ($3 >= 1000) print $6}' < /etc/passwd)
IFS=$'\n' #Use newline as delimiter for for-loop
for usrLine in $usrInfo
do
#Do processing on each line
echo $usrLine
done
または、コメントで提案されているように完全なリストが利用できない場合にgetent passwd
使用できます。/etc/passwd
ジュエリーに感謝します。 :)
usrInfo=$(getent passwd | awk -F: '{if ($3 >= 1000) print $6}')
ただし、オペレーティングシステムによっては検査方法が>= 1000
異なる場合があります。
答え3
私は@gilles Awesomeの答えをテンプレートとして使って、私にとってうまくいきます。 zshを使用してUbuntu 16.04でテストされました。
# get all users
getent passwd | while IFS=: read -r name password uid gid gecos home shell; do
# only users that own their home directory
if [ -d "$home" ] && [ "$(stat -c %u "$home")" = "$uid" ]; then
# only users that have a shell, and a shell is not equal to /bin/false or /usr/sbin/nologin
if [ ! -z "$shell" ] && [ "$shell" != "/bin/false" ] && [ "$shell" != "/usr/sbin/nologin" ]; then
echo "$name's home directory is $home using shell $shell"
fi
fi
done