【问题标题】:Shell - Looping Array with command and increment command valuesShell - 带有命令和递增命令值的循环数组
【发布时间】:2015-08-14 23:59:30
【问题描述】:
    var1=$(echo $getDate | awk '{print $1} {print $2}')
    var2=$(echo $getDate | awk '{print $3} {print $4}')
    var3=$(echo $getDate | awk '{print $5} {print $6}')

我不需要像上面的代码那样重复,而是:

  1. 循环相同的命令
  2. 增加值({print $1} {print $2})
  3. 将值存储在数组中

我正在做类似下面的事情,但我卡住了,也许有人可以帮助我:

COMMAND=`find $locationA -type f | wc -l`
getDate=$(find $locationA -type f | xargs ls -lrt | awk '{print $6} {print $7}')
a=1
b=2
for i in $COMMAND
do
   i=$(echo $getDate | awk '{print $a} {print $b}')
   myarray+=('$i')
   a=$((a+1))
   b=$((b+1))
done

PS - 使用 ksh

问题:$COMMAND 存储在 $locationA 中找到的文件数。我需要遍历找到的文件数量并将它们的日期存储在一个数组中。

【问题讨论】:

  • 你使用的是哪个外壳?
  • 参见mywiki.wooledge.org/ParsingLsmywiki.wooledge.org/BashFAQ/087 -- 使用xargs ls绝对提取元数据的错误方法。 (以安全和便携的方式查找 N 个最新文件本身就是一个常见问题解答,并且有很好的解决方案。
  • 此外,从findxargs ls 的管道将在每批文件中调用一次ls,其中批大小取决于您平台的MAX_ARGS,而ls -t 仅在这些批中排序(因为一个调用只能对给出调用的参数进行排序).. 总而言之,除了上面链接中给出的错误之外,你在这里所做的 also 没有如果文件数量超过一定数量,则按时间正确排序。
  • @anubhava,显然 shell 有数组和 += 语法,所以我假设它是 bash(并且没有被错误标记为这样)。
  • 顺便说一句,for i in $COMMAND 也被严重破坏了。另见mywiki.wooledge.org/DontReadLinesWithFor——但是,既然你正在输入wc -l,那么$COMMAND中只会有一个东西,一个数字。迭代仅包含单个数字的变量的字符串拆分版本有什么意义?

标签: arrays shell unix scripting


【解决方案1】:

我不明白您的示例代码的含义('for' 循环应该做什么?变量 COMMAND 的内容是什么?),但是在您的问题中,您要求将某些内容存储在 array,虽然在您希望简化的代码中,您不使用数组,而是使用简单的变量(var1、var2、...)。

如果我正确理解您的要求,您的变量 getDate 包含一个由多个单词组成的字符串,这些单词以空格分隔,并且您希望将前两个单词分配给 var1,将后面的两个单词分配给 var2,依此类推。这是正确的吗?

【讨论】:

  • 抱歉忘记包含它 - 在上面的代码中更新了它,基本上 $COMMAND 存储在某个位置找到的文件数。 For 循环将循环查找找到的文件数量(已存储在 $COMMAND 中)
  • 究竟是什么意思,“循环文件的数量”?正如我已经指出的那样,您的循环将只执行一次。
  • 所以如果它找到 3 个文件(存储在 $COMMAND 中)它会循环 3 次.. 也许我没有以正确的格式写入
  • @Sergio MS:你写它的方式,'wc -l' 从标准输入获取输入,这意味着输出总是一个数字,例如数字 3。因此,'for i in 3' 将被执行,这意味着循环的单次执行,其中 i 设置为数字 3。
  • 是的,我明白这一点,我可以同意这一点.. 所以在你看来,我该如何创建一个 for 循环来循环,直到它与找到的文件数匹配?
【解决方案2】:

现在编辑后的代码至少清晰了一点,虽然我还是不明白,为什么你使用 i 作为循环变量,并在循环内的第一条语句中覆盖它。

但是,有几个cmets:

  • 如果将“$i”推入数组,您将得到一个文字“$”符号,后跟字母“i”。要将包含 i 的变量添加到数字中,您需要双引号 ("$i")。

  • 我不明白您为什么要遍历变量 COMMAND 的内容。此变量将始终保存一个数字,这意味着循环将只执行一次。

  • 您可以使用计数循环,在每次迭代时将循环变量递增 2。您必须预先计算迭代次数。

  • 也许一个更简单的替代方案,可以在 bash 或 zsh 中工作(我没有尝试其他 shell)是首先将变量转换为数组,

    tmparr=($(echo $getDate|fmt -w 1))

然后使用循环来收集这个元素的对:

myarray=()
for ((i=0; i<${#tmparr[*]}; i+=2))
do
    myarray+=("${tmparr[$i]} ${tmparr[$((i+1))]}")
done

${myarray[0]} 将保存一个字符串,该字符串由 getDate 等中的 first to 单词组成。

【讨论】:

  • foo=( $(bar) ) 不是填充数组的最佳实践方法——它不仅在 IFS 上进行字符串拆分,而且还扩展了 glob,从而尊重 nullglob 设置。因此,如果bar 输出*,您将在foo 中获得当前目录中的文件名列表;如果它包含[something],并且如果没有匹配项并且启用了nullglob 选项,则表达式所属的单词将被完全删除!使用read -a 读入数组要可靠得多。
  • @Charles Duffy:感谢您指出这一点。如果您也有时间查看我的 zsh 解决方案(OP 使用的语法不排除 zsh,所以我认为为此提供解决方案也很有意义),尤其是对于第二个解决方案。由于 (f) 修饰符,我认为这可以避免您描述的陷阱。这是正确的吗?
  • 坦率地说,这是我十多年前停止使用 zsh 的主要原因:偏离 POSIX shell 的方式使得在 zsh 中正常工作的东西在 shell 中存在细微的错误(但不是彻底损坏!)符合 POSIX 扩展规则的人习惯于在迁移到其他 shell 时让他们遇到麻烦的实践/习惯——但足够微妙的麻烦以至于他们的错误经常通过测试。
  • 嗯,zsh 确实不兼容 POSIX - 我个人只是将它视为一种不同的编程语言,最近开始用 zsh 编写我的大部分 shell 内容,只是有几件事我喜欢比在 POSIX shell 中更好。但是,在讨论这些问题时,我们确实应该始终建立上下文,即我们正在使用哪个 shell。在许多情况下,甚至有必要说明应该使用哪个版本的 shell .....
【解决方案3】:

这个应该可以在 zsh 上运行,至少在较新的版本上:

myarray=()
echo $g|fmt -w 1|paste -s -d " \n"|while read s; do myarray+=("$s"); done

这会留下 ${myarray[1]} 中的第一对,等等。

它不适用于 bash(和旧的 zsh 版本),因为这些 shell 会在子 shell 中执行循环体。

添加:

再想一想,在 zsh 中这个会更简单:

myarray=("${(f)$(echo $g|fmt -w 1|paste -s -d ' \n')}")

【讨论】:

  • 您可以很容易地采用您的解决方案来使用现代 bash,因为它发生了。 while read -r s; do myarray+=( "$s"); done &lt; &lt;(fmt -w1 &lt;&lt;&lt;"$g" | paste -s -d " \n"
  • ...虽然使用 readarraymapfile 会更容易,但如果针对 bash 4:readarray -t myarray &lt; &lt;(fmt -w1 &lt;&lt;&lt;"$g" | paste -s -d ' \n')
  • ...或者您可以shopt -s lastpipe(同样,在最近的 bash 4.x 中)将循环的右侧放在主 shell 中(受到一些限制,最明显的是不兼容有工作控制);那时,您只需要在 echo "$g" 周围加上引号,即可在 bash 中使用您的股票第一个答案。
  • 太棒了!我什至不知道 readarray 存在(我只是在手册页中检查了它)!
  • @Sergio M.S.:我不知道。我已经很久没有用 ksh 编程了,尤其是我不知道最新版本的功能。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-20
  • 1970-01-01
  • 1970-01-01
  • 2014-01-20
  • 2015-01-26
相关资源
最近更新 更多