当前位置:  开发笔记 > 编程语言 > 正文

Bash:我可以在不使用"eval"的情况下评估非常数范围内数字的格式字符串吗?

如何解决《Bash:我可以在不使用"eval"的情况下评估非常数范围内数字的格式字符串吗?》经验,为你挑选了1个好方法。

我不乐意使用eval,但我尝试的其他所有内容都因{a..b}语法限制而失败.这就是我所拥有的,是的,我知道我可以将这两个循环结合起来,但eval已经足够丑陋了.

cores=""
suffixes=""
np=$(nproc --all)
eval 'for i in {1..'$np'} ; do cores="$cores $i"; done'
for i in $cores ; do
  suffixes="$suffixes "$(printf %02i $i)
done

Charles Duff.. 7

首先,一个积极的现代解决方案:

#!/bin/bash
#      ^^^^- "printf -v" and C-style for loops both require bash, not /bin/sh

np=$(nproc --all)
cores=( )
for ((i=0; i

这会生成一个索引数组:其键是核心数,其值是后缀字符串.因此,您可以迭代"${!cores[@]}"以获取核心数列表; 在"${cores[@]}"获得后缀字符串列表,或使用"${cores[$i]}"查找后缀为核心$i.


接下来,一个更接近原始代码的解决方案,为现代bash构建:

#!/bin/bash
#      ^^^^- "printf -v" and C-style for loops both require bash, not /bin/sh

np=$(nproc --all)
cores=""; suffixes=""
for ((i=0; i

您还可以仅在数组中构建核心数字,并在一个步骤中计算后缀数字:

# read cores from string into an array to allow safe evaluation even with unknown IFS
IFS=' ' read -r -a cores_arr <<<"$cores"
# ...and expand the full array, repeating the format string for every element
printf -v suffixes '%02i ' "${cores_arr[@]}"

值得注意的是:

迭代扩展数组,即.for i in $cores,通常是不好的做法 - 如果你的价值保证只是数字,它可以是安全的,但要注意副作用:

字符串中的Glob表达式被扩展:如果你以某种方式*在数据中有一个,你会发现自己在当前目录中迭代文件.

字符串拆分不允许对数组所执行的元素边界进行细粒度控制.您必须array=( "item one" "item two" )存储两个项目,其名称中包含空格; 如果你尝试设置string=' "item one" "item two" ',你会得到"item一个单词,one"作为第二个单词,等等.

因此,迭代数组元素 - 即使这意味着从字符串读取数组 - 是非常优选的.

循环使用任意数量的项目最好使用C风格的for循环.

在上面,除了以外没有外部命令nproc.这意味着我们不依赖于非POSIX工具seq.

使用printf -v suffixprintf直接执行的字符串格式化操作的结果写入名为的变量suffix.(旁白:ksh93没有printf -v,但识别内部使用printf $()并避免子shell惩罚).请参阅bash-hackers页面printf.

因此:除了运行所述外部命令并捕获其输出所需的子设备之外,没有其他子设备.(每个子fork()shell在使用mkfifo()生成FIFO以捕获其输出之后需要关闭shell的另一个副本;读取该输出; wait()输入子shell以退出等;因此,它们最好保持在紧密循环之外).


相比之下,如果你需要与POSIX sh的兼容性,那么我们仍然有$(( ))但没有(( ))(+=除了数学上下文之外没有任何操作,根本没有C风格的for循环).这让我们:

#!/bin/sh
build_suffix() {
  np=$1; i=0
  while [ "$i" -lt "$np" ]; do
    printf '%02i ' "$i"
    i=$((i+1))
  done
}
suffixes=$(build_suffix "$(nproc --all)")

...通过将整个循环放在单个子壳内,无论我们循环多少次,它都能给出一个恰好两个子壳的答案.



1> Charles Duff..:

首先,一个积极的现代解决方案:

#!/bin/bash
#      ^^^^- "printf -v" and C-style for loops both require bash, not /bin/sh

np=$(nproc --all)
cores=( )
for ((i=0; i

这会生成一个索引数组:其键是核心数,其值是后缀字符串.因此,您可以迭代"${!cores[@]}"以获取核心数列表; 在"${cores[@]}"获得后缀字符串列表,或使用"${cores[$i]}"查找后缀为核心$i.


接下来,一个更接近原始代码的解决方案,为现代bash构建:

#!/bin/bash
#      ^^^^- "printf -v" and C-style for loops both require bash, not /bin/sh

np=$(nproc --all)
cores=""; suffixes=""
for ((i=0; i

您还可以仅在数组中构建核心数字,并在一个步骤中计算后缀数字:

# read cores from string into an array to allow safe evaluation even with unknown IFS
IFS=' ' read -r -a cores_arr <<<"$cores"
# ...and expand the full array, repeating the format string for every element
printf -v suffixes '%02i ' "${cores_arr[@]}"

值得注意的是:

迭代扩展数组,即.for i in $cores,通常是不好的做法 - 如果你的价值保证只是数字,它可以是安全的,但要注意副作用:

字符串中的Glob表达式被扩展:如果你以某种方式*在数据中有一个,你会发现自己在当前目录中迭代文件.

字符串拆分不允许对数组所执行的元素边界进行细粒度控制.您必须array=( "item one" "item two" )存储两个项目,其名称中包含空格; 如果你尝试设置string=' "item one" "item two" ',你会得到"item一个单词,one"作为第二个单词,等等.

因此,迭代数组元素 - 即使这意味着从字符串读取数组 - 是非常优选的.

循环使用任意数量的项目最好使用C风格的for循环.

在上面,除了以外没有外部命令nproc.这意味着我们不依赖于非POSIX工具seq.

使用printf -v suffixprintf直接执行的字符串格式化操作的结果写入名为的变量suffix.(旁白:ksh93没有printf -v,但识别内部使用printf $()并避免子shell惩罚).请参阅bash-hackers页面printf.

因此:除了运行所述外部命令并捕获其输出所需的子设备之外,没有其他子设备.(每个子fork()shell在使用mkfifo()生成FIFO以捕获其输出之后需要关闭shell的另一个副本;读取该输出; wait()输入子shell以退出等;因此,它们最好保持在紧密循环之外).


相比之下,如果你需要与POSIX sh的兼容性,那么我们仍然有$(( ))但没有(( ))(+=除了数学上下文之外没有任何操作,根本没有C风格的for循环).这让我们:

#!/bin/sh
build_suffix() {
  np=$1; i=0
  while [ "$i" -lt "$np" ]; do
    printf '%02i ' "$i"
    i=$((i+1))
  done
}
suffixes=$(build_suffix "$(nproc --all)")

...通过将整个循环放在单个子壳内,无论我们循环多少次,它都能给出一个恰好两个子壳的答案.

推荐阅读
ifx0448363
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有