我是 Bash 脚本的新手。
尝试编写一个简单的脚本,从标准输入(文件)读取并写入标准输出。我希望该脚本计算参数/算术扩展和命令替换的数量。
对于标准输入我到目前为止已经完成了以下操作:
file=${1--}
while IFS= read -r line; do
printf '%s\n' "$line"
done < <(cat - "$file")
答案1
实用方法
只需使用grep
标志-c
进行计数,或者更好的grep -o '<PATTERN>' | wc -l
方法(请参阅后面的注释)。根据您希望输出的样子以及您想要实现的目标,有多种设置方法。
当然,参数扩展类型有很多种,所以我们需要为它们编写不同的模式。这里我只写了替换、前缀删除和算术扩展模式,但你可以将它们改编为任何其他模式。
在最基本的层面上,我们可以像这样调用单个命令:
$ cat ./myscript.sh
#!/bin/bash
var="string"
echo "${var/string/thread}"
echo $((2+2))
echo $((3*2))
echo ${var#*i}
echo ${var##*i}
echo ${var%i*}
echo ${var%%i*}
$ grep -c '${.*}' myscript.sh
1
$ grep -c '$((.*))' myscript.sh
2
这只是基本想法。您可以将多个grep
模式放入一个脚本中,并带有良好的引导信息。例如:
#!/usr/bin/env bash
printf "\nNumber of arithmetic expansions:"
grep -c '$((.*))' "$1"
printf "\nNumber of substitutions:"
grep -c '${.*\/.*\/.*}' "$1"
printf "\nNumber of prefix removals:"
grep -c '${.*[#]\{1,2\}.*}' "$1"
然后像这样调用该脚本:
./count_bash_stuff.sh myscript.sh
或者,只需将所有模式放入文件中并调用grep
即可-f
。当然,它只会输出所有比赛的总数不是个别的:
$ cat ./patterns
$((.*))
${.*\/.*\/.*}
${.*[#]\{1,2\}.*}
$ grep -c -f ./patterns ./myscript.sh
5
注意:正如steeldriver 在本答案下方的评论中指出的那样,grep
是一个行匹配工具,这意味着如果一行中有多个模式,它将为该行添加 1 计数。更好的方法是使用-o
标志grep
并将输出传送到wc -l
。因此,我们可以对算术扩展执行类似这样的操作:
grep -o '$((.*))' | wc -l
POSIX shell 方式(不太实用)
是的,这是可能的。这很难,但使用case
语句是可能的。这里我只会写出case
算术扩展和最长前缀删除的语句。其余的就交给读者去适应和扩展吧。
再次提醒,请记住这只是概念验证。请注意,这使用的是/bin/sh
- 在 Ubuntu 上,这是符合 POSIX 标准的/bin/dash
shell,而不是bash
。
#!/usr/bin/env sh
while IFS="" read -r line || [ -n "$line" ];
do
case "$line" in
# arithmetic expansion, appearing anywhere in line
*'$(('*'))'*) arithmetic_count=$(($arithmetic_count+1));;
*'${'*##*'}'* ) prefix_count=$((prefix_count+1));;
esac
done < "$1"
echo "$arithmetic_count"
echo "$prefix_count"
它的运行方式如下:
$ ./count_shell_stuff.sh myscript.sh
2
1
你可能会问,为什么这不太实用?因为在 中编写模式case
可能有些困难,而且比在grep
或中编写模式要难一些Perl
。另外,你必须case
为每个 shell 语法结构编写模式。使用grep
或 ,perl
你可以将多个模式组合成一个。但话又说回来,这只是我个人的看法。正如我在这个小概念证明中所展示的那样,字符串模式匹配仍然非常有可能,但我不建议这样做。