在 Bash 中使用变量和带有数组的 for 循环

在 Bash 中使用变量和带有数组的 for 循环

我在 shell 脚本中声明了一个名为 arrUsers 的数组。我正在尝试使用最后一个命令获取所有登录到 Linux 服务器的用户。我想做这样的事情:

declare -a arrUsers=()
for user in `last | awk '{print $1}'
do
    if $user not in arrUsers()
        do arrUsers += users

我不太确定执行此操作的正确语法是什么。任何帮助将不胜感激。

答案1

我在这里看到两个不同的点:获取成功登录的用户列表 (1) 并将该列表添加到 Bash 数组 (2)。

  1. 解析last的输出并不简单,因为它还包含不以用户名开头的行。 Linux 上有一些替代方案:

    lslogins -r | awk -F '[ ]' 'NR > 1 && $6 { print $2 }'
    

    lslogins显示有关用户的信息(不是登录事件),因此每个用户仅输出一行。该-r选项告诉它不要对输出进行分列,允许我们假设单个空格作为列分隔符,并利用 的awk能力来避免在FS不是单个空格(默认)时挤压空格字符。不过,我不确定 的任何lslogins输出字段的内容中不会出现空格字符。

    或者:

    utmpdump /var/log/wtmp 2>/dev/null |
      gawk -v FPAT='\\[[^][]*\\]' '
        $1 == "[7]" { gsub(/[][]/,"",$4); sub(/ +$/,"",$4); users[$4] }
        END { for (user in users) print user }'
    

    这使用非标准 GNU AWK 的功能根据FPAT描述字段内容的正则表达式(存储在 )来分割记录。在 的输出中utmpdump,字段括在方括号中并用一个空格分隔。我们根据存储在第一个字段中的类型来过滤记录(man 5 wtmp有关更多信息,请参阅 参考资料)。

    请注意,虽然创建这样的名称绝对不是一个明智的选择,但 Linux 允许用户名不符合标准并包含不在其中的字符可移植文件名字符集——包括空格。空格字符不会成为 的问题lslogins -r,它会转义它们(如\x20),但尾随空格不会在utmpdump的输出中明确表示,并且上面的代码片段会删除它们。

  2. 您可以使用以下方法将唯一用户名列表放入数组中:

    readarray -t users \
      < <(lslogins -r | awk -F '[ ]' 'NR > 1 && $6 { print $2 }')
    

    或者,将元素添加到现有数组而不重置它:

    readarray -t -O "${#users[@]}" users \
      < <(lslogins -r | awk -F '[ ]' 'NR > 1 && $6 { print $2 }')
    

    不过,这些并不能保证元素的唯一性。为了实现这一点,您可能需要几个循环:

    while IFS= read -r user
    do
      for el in "${users[@]}"
      do
        if [ "$el" = "$user" ]
        then
          continue 2
        fi
      done
      users+=( "$user" )
    done < <(lslogins -r | awk -F '[ ]' 'NR > 1 && $6 { print $2 }')
    

    作为替代方案(如果您的 Bash 版本支持),您可以使用关联数组并将用户名存储为键,确保每个用户名仅出现一次:

    declare -A users
    while IFS= read -r user
    do
      users["$user"]=''
    done < <(lslogins -r | awk -F '[ ]' 'NR > 1 && $6 { print $2 }')
    

    然后您可以使用以下语法迭代它们"${!array[@]}"

    for username in "${!users[@]}"
    do
      printf '%s\n' "$username"
    done
    

相关内容