如何在 shell 脚本中循环遍历所有用户?
我正在编写一个 shell 脚本来在系统上执行清理。如果脚本以 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
您可以测试用户是否拥有一个真正的 shell。但许多系统用户也这样做,因此它的可靠性甚至比主目录还要低。
您可以测试该账户是否有密码。这并不完全可靠,因为实际用户并不总是有密码(例如他们可能只有 SSH 密钥)。有时系统帐户有密码——特别是 root 帐户经常有。如何执行此操作取决于 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