【问题标题】:Bash script beginner questions: Looping, Arrays and character checksBash 脚本初学者问题:循环、数组和字符检查
【发布时间】:2017-01-25 17:21:28
【问题描述】:

我正在上一门让我们用 bash 编写脚本的课程,而且我对 bash 很陌生。

我的教授给了我们一个作业: “编写一个 Bash 脚本 count.sh,它为 /bin 下以每个字母开头的命令构建一个计数表。例如,如果有 3 个以“a”开头的命令(alsaumute、arch & awk)而可能是 2 个以“z”开头的命令(zcat 和 zsh)。您的脚本将打印的第一行和最后一行是: 一个 3 ... z 2"

所以我一直在解决这个问题并且我能够设置循环,但我不清楚我应该如何检索 bin 命令(我假设 bin 是一个文件只是 bash 的命令列表?)然后对第一个字符执行字符检查?

他告诉我使用 ls 和 grep 作为提示。我查找了 ls(列出文件/目录)并 grep 搜索特定文本,所以我假设我使用 ls 以某种方式获取 bin 命令,然后在循环中对它们执行 grep?

declare -a letters=('a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 'u' 'v' 'w' 'x' 'y' 'z')

counter=0
         while [  $counter -lt 26 ]; do
            current=${letters[$counter]}
             echo The counter is $counter
             echo $current
             let counter=counter+1 
         done

这就是我到目前为止的位置,所以我的猜测是我创建了一个变量数组来保存所有 bin 命令(使用 ls 对吗?),然后在循环中使用 grep?

我只是想得到一些建议,说明我走在正确的道路上。我对 linux 很陌生,从来没有处理过脚本或 cmd 行类型的东西。

Here's the output so far

【问题讨论】:

  • 我假设 bin 是一个仅包含 bash 命令列表的文件?’ 你知道当你假设时他们会说什么吗?这种假设是非常错误的。
  • 试试ls -l /bin/
  • @sherrellbc Or don't
  • 你的教授给了你不好的建议。不推荐使用lsgrep;见Why you shouldn't parse the output of ls(1)
  • @Biffen,“试试ls -l /bin/”看看/bin/ 是什么意思。

标签: linux bash loops


【解决方案1】:

如果使用 bash 4.0 或更高版本,以下是使用 only 内置于 shell 中的功能的实现 -- 没有 ls,没有 cut,没有 grep,没有 @987654328 @等

#!/usr/bin/env bash
#              ^^^^- must be run with bash, 4.0 or newer; NOT /bin/sh.

declare -A counts=( )            # declare an associative array mapping letters to counts
for entry in /bin/*; do          # use a glob to list filenames in /bin
  filename=${entry##*/}          # strip the path off the beginning of each name
  first_char=${filename:0:1}     # take the first character of what's left
  (( counts[$first_char] += 1 )) # and update the associative array's counter
done

# ...then, iterate over the keys in the associative array...
for first_char in "${!counts[@]}"; do
  # ...and print them alongside their associated values (the counts)
  printf '%s %s\n' "$first_char" "${counts[$first_char]}"
done

你也可以用declare -p counts查看这个构建的关联数组;它看起来像下面这样(取自最有趣的东西在/usr/bin而不是/bin的系统,所以在下面的例子中相当稀疏):

declare -A counts=([b]="1" [c]="4" [d]="4" [e]="3" [h]="1" [k]="2" [l]="4" [m]="2" [p]="3" [r]="2" [s]="4" [t]="2" [u]="1" [w]="1" [z]="1" )

一些注意事项:

  • 一般来说,shell 内建函数比启动外部工具来处理单个值要快(很多!),但比让外部工具处理一长串值要慢。命令替换和管道都启动子进程——要求 shell 在内存中创建自己的新副本,其中一些副本随后用外部可执行文件的实例替换自己。这是相当大的开销,而不是您想在紧密循环中执行的操作。
  • 特别是ls 是为人类消费而不是机器解析其输出而设计和构建的。 (由于ls 以换行符分隔的形式发出其输出,并且普通UNIX 系统上的文件名可以包含文字换行符,因此ls 不可能以文字形式表示所有可能的文件名!)。避免在脚本和其他目标不是人类消费的场景中使用它。
  • declare -A 是数组关联的原因,这意味着它的键可以是任意字符串,而不仅仅是正整数。这是一个相对较新的功能,这也是为什么上述功能仅与 bash 4.0 或更高版本兼容的原因。
  • "${array[@]}" 迭代该数组的值,而 "${!array[@]}" 迭代其键。如您所知,"${array[$key]}" 可用于从键映射到关联值。请参阅the relevant bash-hackers page for more on arraysBashFAQ #5
  • ${entry##*/}parameter expansion —— bash 中最强大的本地字符串操作工具之一。这个特殊的修剪从开始到最后一个/ 找到的所有内容。 ${filename:0:1} 是另一个,从位置 0 开始的文件名中取一个字符。
  • (( )) 创建一个 arithmetic context,其中可以使用本机 C 风格的数学语法(仅适用于整数数学)。

【讨论】:

  • @DavidC.Rankin,嗯? declare -A counts 行是 declare -p counts 的示例输出。
  • 谢谢,这是一个很好的答案,虽然我仍在尝试消化它,但它提供的信息量很大,而不仅仅是一个解决方案。
【解决方案2】:

懒惰快乐!

ls -1 /bin|cut -c1|uniq -c
  1 a
 27 b
 10 c
 ...

【讨论】:

  • ls -1 是您将ls 输出发送到终端以外的目的地时的默认值。
  • 如果文件名包含换行符,这将失败。 (并且这样的文件名是否应该包含换行符无关紧要;事实是可以。)
  • @chepner 告诉我一个 Linux 发行版,它在 /bin 中使用基于换行符的文件名。
  • @IporSircer 说出一个禁止它的名字。
  • 命名一个禁止将 /bin/bash 更改为 /bin/false 那么这里所有的解决方案都是错误的。
【解决方案3】:

要获取命令列表,您可以使用compgen -c。将其传送到grep,以查找以字母开头的命令。要获取计数,您可以将该输出通过管道传输到 awk,并使用 NF 变量查找命令数。

declare -a letters=('a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 'u' 'v' 'w' 'x' 'y' 'z')

for current in "${letters[@]}"; do
    echo "$(compgen -c | grep "^$current" | wc -l) $current"
done

【讨论】:

  • compgen 不限于检查或要求检查/bin 的内容。
  • 确实如此。这解决了分配应该是什么,而不是分配
  • 我把它做成了一个社区维基,因为我刚刚测试过它,但它不起作用。到 awk 的管道显然给出了 1 的输出。我想弄清楚出了什么问题
  • @RichardHamilton,解释为什么这不起作用 -- NF 是字段数,而不是行数。
  • @CharlesDuffy 我知道,但是当我通过脚本查看输出时,结果用空格分隔。这就是为什么我将分隔符放在空格中。
猜你喜欢
  • 1970-01-01
  • 2013-09-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-12
  • 2015-10-15
相关资源
最近更新 更多