我试图找到最有效的方法来遍历某些值,这些值是在空格分隔的单词列表中彼此相距一致数量的值(我不想使用数组)。例如,
list="1 ant bat 5 cat dingo 6 emu fish 9 gecko hare 15 i j"
所以我希望能够只遍历列表并且只访问 1、5、6、9 和 15。
编辑:我应该明确表示我试图从列表中获取的值在格式上不必与列表的其余部分不同。使它们与众不同的仅仅是它们在列表中的位置(在这种情况下,位置 1,4,7...)。所以列表可能是1 2 3 5 9 8 6 90 84 9 3 2 15 75 55
,但我仍然想要相同的数字。而且,假设我不知道列表的长度,我希望能够做到这一点。
目前我想到的方法有:
方法一
set $list
found=false
find=9
count=1
while [ $count -lt $# ]; do
if [ "${@:count:1}" -eq $find ]; then
found=true
break
fi
count=`expr $count + 3`
done
方法二
set list
found=false
find=9
while [ $# ne 0 ]; do
if [ $1 -eq $find ]; then
found=true
break
fi
shift 3
done
方法 3 我很确定管道使这是最糟糕的选择,但出于好奇,我试图找到一种不使用 set 的方法。
found=false
find=9
count=1
num=`echo $list | cut -d ' ' -f$count`
while [ -n "$num" ]; do
if [ $num -eq $find ]; then
found=true
break
fi
count=`expr $count + 3`
num=`echo $list | cut -d ' ' -f$count`
done
那么什么是最有效的,或者我错过了一个更简单的方法?
软件优化的第一条规则:不要。
在您知道程序的速度是一个问题之前,没有必要考虑它有多快。如果您的列表大约有这个长度或只有大约 100-1000 项,您可能甚至不会注意到需要多长时间。您可能会花更多时间考虑优化而不是差异。
第二条规则:测量。
这是找出答案的可靠方法,也是为您的系统提供答案的方法。尤其是贝壳,有很多,而且它们并不完全相同。一个外壳的答案可能不适用于您的外壳。
在较大的程序中,分析也在这里。最慢的部分可能不是你认为的那个。
三、shell脚本优化的第一条规则:不要使用shell。
是的,真的。许多 shell 的速度并不快(因为不必启动外部程序),它们甚至可能每次都重新解析源代码的行。
改用 awk 或 Perl 之类的东西。在我做的一个微不足道的微基准测试中,
awk
运行一个简单的循环(没有 I/O)比任何常见的 shell 快几十倍。但是,如果您确实使用 shell,请使用 shell 的内置函数而不是外部命令。在这里,您使用
expr
的不是我在系统上找到的任何 shell 中内置的,但可以用标准算术扩展替换。例如i=$((i+1))
,而不是i=$(expr $i + 1)
增加i
. 您cut
在上一个示例中的使用也可以用标准参数扩展替换。另请参阅:为什么使用 shell 循环处理文本被认为是不好的做法?
步骤 #1 和 #2 应该适用于您的问题。
很简单
awk
。这将为您提供任意长度输入的每四个字段的值:这可以利用内置
awk
变量,例如NF
(记录中的字段数),并进行一些简单for
的循环以沿字段进行迭代,从而为您提供所需的字段,而无需提前知道会有多少。或者,如果您确实只想要示例中指定的那些特定字段:
至于关于效率的问题,最简单的方法是测试这个或你的其他方法中的每一个,并用
time
它来显示需要多长时间;您还可以使用诸如strace
查看系统调用流程之类的工具。外观的用法time
如下:您可以比较不同方法之间的输出,以查看哪种方法在时间上最有效;其他工具可用于其他效率指标。
我只会在这个答案中给出一些一般性的建议,而不是基准。基准测试是可靠回答有关性能问题的唯一方法。但是由于您没有说明您正在处理多少数据以及执行此操作的频率,因此无法进行有用的基准测试。对于 10 个项目,什么更有效,对于 1000000 个项目,什么更有效往往是不一样的。
作为一般的经验法则,只要纯 shell 代码不涉及循环,调用外部命令比使用纯 shell 构造执行操作更昂贵。另一方面,迭代大字符串或大量字符串的 shell 循环可能比调用专用工具要慢。例如,您的循环调用
cut
在实践中可能会明显变慢,但是如果您找到一种方法来通过一次cut
调用完成整个事情,这可能比在 shell 中使用字符串操作执行相同的事情更快。请注意,系统之间的截止点可能会有很大差异。它可能取决于内核、内核调度程序的配置方式、包含外部可执行文件的文件系统、当前的 CPU 与内存压力以及许多其他因素。
expr
如果您完全关心性能,请不要致电执行算术。事实上,根本不要调用expr
来执行算术。Shell 有内置的算术运算,比调用expr
.您似乎正在使用 bash,因为您使用的是 sh 中不存在的 bash 结构。那么到底为什么不使用数组呢?数组是最自然的解决方案,也可能是最快的。请注意,数组索引从 0 开始。
如果您使用 sh,如果您的系统使用 dash 或 ksh
sh
而不是 bash,您的脚本可能会更快。如果使用 sh,则不会获得命名数组,但仍会获得位置参数之一的数组,您可以使用set
. 要访问直到运行时才知道位置的元素,您需要使用eval
(注意正确引用事物!)。如果您只想访问一次数组并且从左到右(跳过一些值),您可以使用
shift
变量索引来代替。哪种方法更快取决于外壳和元素的数量。
另一种可能性是使用字符串处理。它的优点是不使用位置参数,因此您可以将它们用于其他用途。对于大量数据,它会变慢,但对于少量数据,这不太可能产生明显的差异。
awk
如果您可以在 awk 脚本中完成所有处理,那么这是一个不错的选择。否则,您最终只会将 awk 输出通过管道传输到其他实用程序,从而破坏awk
.bash
如果您可以将整个列表放入数组中(这对于现代 shell 可能是一种保证) ,并且您不介意数组语法体操,那么对数组的迭代也很棒。但是,管道方法:
在哪里:
xargs
将空格分隔的列表分组为三个批次,每个换行符分隔while read
使用该列表并输出每个组的第一列grep
过滤第一列(对应于原始列表中的每三个位置)在我看来,提高了可理解性。人们已经知道这些工具的作用,因此很容易从左到右阅读并推断将要发生的事情。这种方法还清楚地记录了步长 (
-n3
) 和过滤器模式 (9
),因此很容易可变:当我们问“效率”的问题时,一定要考虑“总寿命效率”。该计算包括维护人员保持代码正常运行的努力,而我们肉袋是整个操作中效率最低的机器。
也许这个?
如果您想提高效率,请不要使用 shell 命令。限制自己使用管道、重定向、替换等和程序。这就是存在实用程序的原因
xargs
-parallel
因为 bash while 循环效率低下且非常缓慢。仅将 bash 循环用作最后的解决方案。但是使用 good 可能会更快一些
awk
。在我看来,最清晰的解决方案(也可能也是最高效的)是使用 RS 和 ORS awk 变量:
使用GNU
sed
和POSIX shell 脚本:或者用
bash
's参数替换:非GNU(即 POSIX)
sed
,并且bash
:或者更便携,同时使用POSIX
sed
和 shell 脚本:任何这些的输出: