【问题标题】:Basic Linux Bash Scripting while / for基本 Linux Bash 脚本 while / for
【发布时间】:2021-12-02 11:12:37
【问题描述】:

在 bash 脚本中迈出第一步。到目前为止,设法进行了 5/7 练习,卡在最后两个练习中,我找不到解决方法。

这里是请求练习的更好翻译,同样应该使用简单的命令来解决,例如 sed、echo、cat、uniq、tr、grep、wc 等:

编写命令行来显示默认 shell 是您机器上可用的 shell 之一的用户数。当命令执行时,对于每个 shell,我们都会得到如下相同形式的结果:

  • 0个用户拥有shell/bin/sh
  • 11个用户有shell/bin/bash
  • 0个用户拥有shell/usr/bin/sh
  • 0个用户拥有shell/usr/bin/bash

@托德

我今天早上做了,你的 grep 重定向输入给了我这个想法。我就是这样做的:

  • result=$(grep -of <(echo $SHELL) /etc/passwd | uniq -c)
  • count=$(echo $result I awk -F” “ '{print $1}')
  • shell=$(echo $result | awk -F” “'{print $2}')
  • echo $result | while read i; do
  • echo $count utilisateurs ont pour shell $shell
  • done

【问题讨论】:

  • 如果某个登录shell不在passwd文件中,怎么可能有0个用户。
  • @Jetchisel 可用的登录shell存储在/etc/shells
  • @Aserre,是的,可能是这种情况,但 OP 没有提到该文件或在代码中发布。
  • @Jetchisel 他们说他们刚刚开始编写 bash 脚本,我怀疑他们不知道这个文件
  • 然而/etc/passwd出现在代码中,最好请OP澄清一下。

标签: linux bash shell script


【解决方案1】:

Unix 实用程序和 Bash 进程替换

在 Bash 中,有很多方法可以做到这一点。但是,从实际的角度来看,您并不真的需要 Bash 本身来解决这个问题。标准的 *nix 工具会做得很好,只要你正确地链接它们。例如,如果输出的特定格式不是重点:

$ grep -of <(grep '^/.*$' /etc/shells) /etc/passwd | sort | uniq -c
   11 /bin/bash
    8 /bin/sh
    4 /usr/local/bin/fish

这基本上是从 /etc/shells 中 grep 你的 shell,然后 grep 将 that 输出视为一个文件来读取表达式以匹配 /etc/passwd。然后它将结果通过管道传送到 sort 和 uniq 实用程序以计算出现次数。它依赖于进程替换,并非所有 shell 都具有或表达相同的方式,但您的问题被标记为 Bash,因此避免 Bashisms 不是主要问题。

但是,由于并非所有 shell 都具有 Bash 所做的进程替换重定向,因此此类解决方案并非 100% 可跨不同 shell 移植。通常有类似的方法可以做到这一点,但有时跨系统和外壳的可移植性确实很重要。无论如何,这显示了 grep、管道和进程替换的强大功能。

获得原始数据后,您可以使用 printf、awk 甚至 sed 格式化输出(如果您希望它采用不同的格式)。但是,如果要计算 shell 使用量,那么在 Bash 或 zsh 中这是一种简单的方法。

更便携的替代方案

更复杂(但也更便携)的替代方法是使用 xargs 作为管道的核心元素。例如:

$ grep -o '^/.*$' /etc/shells |
    xargs -I{} -L1 -- sh -c 'echo "{}"; grep -Fc "{}" /etc/passwd' |
    xargs -n2
/bin/bash 11
/bin/csh 0
/bin/ksh 0
/bin/sh 8
/bin/tcsh 0
/bin/zsh 0
/usr/local/bin/fish 4

这将找到 /etc/shells 中的所有 shell,一次将它们传递给 Bourne 脚本以打印 shell 的路径,并让 grep 的固定字符串匹配器计算 /etc/passwd 中出现的次数,然后通过管道传递再次输出到 xargs 以一次加入两个备份行。

这种方法的主要好处是它会列出所有 shell,无论是否有人使用它们。如果您希望将数字列在 shell 名称之前,您甚至可以将最后一行更改为 tac | xargs -n2

【讨论】:

  • 我今天早上做了,你的 grep 重定向输入给了我这个想法。用代码编辑了第一篇文章。
【解决方案2】:

这就是我理解你的问题的方式,一种使用数组的方法,包括关联数组。

#!/usr/bin/env bash

declare -A Shell_and_Users

while IFS=: read -ra lines; do
  login_shell="${lines[-1]}"
  ((Shell_and_Users["$login_shell"]++))
done < /etc/passwd

检查关联数组Shell_and_Users的值运行:

declare -p Shell_and_Users

现在循环遍历关联数组以打印所需的输出。

for i in "${!Shell_and_Users[@]}"; do
  printf -- '- %d users have shell %s\n' "${Shell_and_Users[$i]}" "$i"
done

如果不是这种情况,则编辑问题并添加更多详细信息,如果文件 /etc/shells 应该被解析/涉及,因为上述解决方案将永远不会打印 0 users

【讨论】:

  • 我不知道我刚刚做了什么,但我复制以试图理解一点。我认为他给出的输出是因为在脚本的某些部分应该运行“cat /etc/passwd | grep “/bin/sh” | wc -l 这将是 0。
  • 好的,如果这是考官/教授给你的,就用cat,但这是UUoC的经典例子,grep -c '/bin/sh$' /etc/passwd也足够了
【解决方案3】:

假设awk可以用,你可以试试这个

$ cat awk.script
#!/usr/bin/env awk -f

BEGIN {
    FS=":"
} $NF=="/bin/bash" {
    count_bash++
    bash=$NF
} $NF=="/usr/bin/bash" {
    count_bash2++
    bash2=$NF
} $NF=="/bin/sh" {
    count_sh++
    sh=$NF
} $NF=="/usr/bin/sh" {
    count_sh2++
    sh2=$NF
} END {
    print count_bash " users have a shell for "bash "\n" \
          count_bash2" users have a shell for "bash2 "\n" \
          count_sh " users have a shell for "sh "\n" \
          count_sh2" users have a shell for "sh2 "\n" \
}

这将提供与您预期的输出相似的输出,但是,如果未使用 shell,您将获得一个没有值或路径的空输出,这可能并不理想。

输出

$ awk -f awk.script /etc/passwd
24 users have a shell for /bin/bash
 users have a shell for
2 users have a shell for /bin/sh
 users have a shell for

【讨论】:

  • 如果你使用awk,你可以用一个关联数组来简化这一切:{c[$NF]++} END {for (s in c) print c[s] " bla " s}。请注意,密码文件有时包含应该跳过的 cmets (#...)。我认为非注释行永远不会包含 #(不是 100% 肯定),因此将 !/#/ 之类的条件添加到 {c[$NF]++} 就足够了。
猜你喜欢
  • 2013-06-15
  • 1970-01-01
  • 2014-12-19
  • 1970-01-01
  • 2013-02-05
  • 1970-01-01
  • 1970-01-01
  • 2020-04-02
  • 1970-01-01
相关资源
最近更新 更多