【问题标题】:Shell script to get list of defined users on Linux?获取Linux上已定义用户列表的Shell脚本?
【发布时间】:2013-05-19 10:21:46
【问题描述】:

我把它放在一起,但它很烂:(例如那里的幻数,文本解析......嘘!)

awk -F: '{if($3 >= 1000 && $3 < 2**16-2) print $1}' /etc/passwd

这样做的正确方法是什么?

【问题讨论】:

  • 文本解析有什么问题?

标签: linux bash sed awk


【解决方案1】:

一些 unix 系统不使用/etc/passwd,或者有没有在那里指定的用户。您应该使用getent passwd 而不是阅读/etc/passwd

我的系统也有一些用户被禁用,他们的登录命令设置为/bin/false/usr/sbin/nologin 时无法再登录。您可能还想排除它们。

这对我有用,包括 arheops awk 命令和 ansgar 的代码,用于从 login.defs 获取最小值和最大值:

getent passwd | \
grep -vE '(nologin|false)$' | \
awk -F: -v min=`awk '/^UID_MIN/ {print $2}' /etc/login.defs` \
-v max=`awk '/^UID_MAX/ {print $2}' /etc/login.defs` \
'{if(($3 >= min)&&($3 <= max)) print $1}' | \
sort -u

【讨论】:

  • 在我的家庭网络上运行良好,尽管 LDAP 用户、自动挂载的主目录具有奇怪的路径等。它确实会为同时具有 LDAP 和后备 /etc/passwd 条目的用户产生重复项,但除此之外,很好.
  • 可以通过附加| sort -u来消除重复。
  • ... 或使用临时数组作为awk 命令的一部分。
【解决方案2】:

我不确定你为什么只做 > 1000,因为在 redhat 系统上它从 500 开始。

对我来说,这个 awk 脚本工作正常:

awk -F: '{if(($3 >= 500)&&($3 <65534)) print $1}' /etc/passwd

仅用于密码:

awk -F: '{if(!(( $2 == "!!")||($2 == "*"))) print $1}' /etc/shadow 

【讨论】:

  • 你完全正确,基地选错了。 1000 是一个愚蠢的“幻数”,我的错……但是……500 也是如此?!或者,不是吗?
  • 这不是必需的,您的链接文档按增加优先级的顺序说明运算符,因此&amp;&amp; 的优先级确实低于&gt;=&lt;
  • 对于 redhat 是 500。看起来 499 之后没有使用服务。但这实际上仅适用于 RHEL。您可以根据系统设置添加“grep /home”或“grep /bin/bash”之类的内容。此外,您只能获得具有密码的用户 - 见上文。
  • 我相信基于 Debian 的系统从 1000 开始。
【解决方案3】:

/etc/login.defs中提取最小和最大用户ID,然后从/etc/passwd中选择ID在这些边距之间的用户:

UID_MIN=$(awk '/^UID_MIN/ {print $2}' /etc/login.defs)
UID_MAX=$(awk '/^UID_MAX/ {print $2}' /etc/login.defs)

awk -F: -v min=$UID_MIN -v max=$UID_MAX '$3 >= min && $3 <= max{print $1}' /etc/passwd

【讨论】:

  • 在旁注中,if 是多余的......只需使用 '$3 &gt;= min &amp;&amp; $3 &lt;= max{print $1}'
  • 这会遗漏很多,在某些情况下,几乎所有使用 LDAP、NIS 和其他用户机制(包括存储在 /etc/passwd 文件之外的数据库)的站点用户。
  • @AlexNorth-Keys 是的。但是,我的印象是他在寻找本地用户。
  • 一些疯狂的人也为本地用户使用 LDAP ;-) 他没有指定是否只使用 /etc/passwd 文件作为源。
【解决方案4】:

这是另一种只生成一个外部程序getent(由@AnsgarWiechers 建议)的方法,以便使用本地和网络密码数据库。这将getent 本身的分叉数量减少到只有一个。然而,由于需要 bash4,它的可移植性受到了一定的限制。

get_users ()
{ 
    local IFS=$' \t#'
    while read var val ; do
        case "$var" in 
            UID_MIN) min="$val" ;;
            UID_MAX) max="$val" ;;
        esac
    done < /etc/login.defs
    declare -A users
    local IFS=:
    while read user pass uid gid gecos home shell; do
        if (( min <= uid && uid <= max )) && [[ ! $shell =~ '/(nologin|false)$' ]]; then
            users[$user]=1
        fi
    done < <(getent passwd 2>/dev/null)
    echo ${!users[@]}
}

【讨论】:

  • +1。您应该提到这需要bash4,所以不是一个非常便携的解决方案。
【解决方案5】:

所以您只是想从 /etc/passwd 获取所有用户的列表?如果是这样,我相信这将是一个更简单的解决方案:

cut -d":" -f1 /etc/passwd

编辑:

如果您只需要用户定义的用户列表(而不是系统用户),您可以使用以下之一:

grep -E ":[0-9]{4,6}:[0-9]{4,6}:" /etc/passwd | cut -d: -f1

^ 这假设您的系统为用户定义的用户使用 1000 及以上的 UID 和 GID

grep /home /etc/passwd | cut -d: -f1

^ 这假设每个用户定义的用户都有一个主目录。

其他解决方案取决于更详细的标准和您的系统设置。

【讨论】:

  • 编辑中的好主意。我学到了一些东西。不过看起来仍然像一个黑客,不是吗?
  • @Robottinosino:不,这只是常规的 shell 脚本 :)
  • 许多站点的用户主目录分散在文件系统中,甚至经常分布在不同主机上的网络中——并不是所有站点都为它们创建 /home 元目录。 /net/server1/fs/d3/users/tom 例如。
【解决方案6】:

这是@StephenOstermiller 的答案的简化,它只需要两个过程就可以完成。我认为它也更容易阅读(前提是您熟悉 awk NR==FNR 成语)。

getent passwd |
awk 'NR==FNR { if ($1 ~ /^UID_(MIN|MAX)$/) m[$1] = $2; next }
{ split ($0, a, /:/);
  if (a[3] >= m["UID_MIN"] && a[3] <= m["UID_MAX"] && a[7] !~ /(false|nologin)$/)
    print a[1] }' /etc/login.defs -

两个输入中的不同拆分模式有点麻烦;也许你可以以某种方式更优雅地解决这个问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-09-20
    • 1970-01-01
    • 1970-01-01
    • 2015-08-27
    • 2015-03-26
    • 1970-01-01
    • 2014-03-14
    • 2021-08-17
    相关资源
    最近更新 更多