【问题标题】:Calling the current and next item in a bash for loop在 bash for 循环中调用当前项和下一项
【发布时间】:2015-05-26 14:53:21
【问题描述】:

我正在进行反复分析,必须向大型计算机集群上的批处理系统提交 5000 多个作业。

我想运行一个 bash for 循环,但同时调用当前列表项和脚本中的下一项。我不确定使用这种格式的最佳方法:

#! /bin/bash
for i in `cat list.txt`; 
do
    # run a bunch of code on $i (ex. Data file for 'Apples')
    # compare input of $i with next in list {$i+1} (ex. Compare 'Apples' to 'Oranges', save output)
    # take output of this comparison and use it as an input for the next analysis of $i (ex. analyze 'Apples' some more, save output for the next step, analyze data on 'Oranges')
    # save this output as the input for next script which analyses the next item in the list {$i+1}  (Analysis of 'Oranges' with input data from 'Apples', and comparing to 'Grapes' in the middle of the loop, etc., etc.)
done

在 while 循环中提供表格输入列表对我来说最简单吗?我真的不想这样做,因为我必须做一些代码编辑,虽然很小。

感谢您对新手的帮助——我浏览了整个互联网并翻阅了一堆书籍,但没有找到一个好的方法。

编辑:出于某种原因,我认为可能有一个 for 循环技巧可以做到这一点,但我猜不是;对我来说,使用表格输入进行 while 循环可能更容易。我准备这样做,但我不想重写我已经编写的代码。

更新:非常感谢大家的时间和投入!非常感谢。

【问题讨论】:

  • 看起来你想要一个递归函数,但我不太明白你的指示。请在循环主体中发布实际命令,即使它们是基本的。
  • 为什么要看下一个项?这需要peek()-type 功能,而 bash 没有。相比之下,查看 previous 项很容易——只需将其存储在一个变量中并在循环中引用该变量即可。 :)
  • 您希望您的配对重叠吗?即1 22 33 4,还是1 23 45 6
  • 改变你的策略,不要使用current和next,而是current and pass one。结果相同,但更容易。

标签: linux bash for-loop


【解决方案1】:

另一个解决方案是使用 bash 数组。例如,给定一个文件list.txt,其内容为:

1
2
3
4
4
5

您可以使用文件的行作为元素创建一个数组变量:

$ myarray=(1 2 3 4 4 5)

虽然您也可以这样做 myarray=( $(echo list.txt) ) 这可能会在空白处拆分并不适当地处理其他输出,但更好的方法是:

$ IFS=$'\n' read -r -d '' -a myarray < list.txt

然后你可以访问元素:

$ echo "${myarray[2]}"
3

数组的长度由${#myarray[@]} 给出。所有索引的列表由${!myarray[@]} 给出,您可以遍历此索引列表:

for i in "${!myarray[@]}"; do 
    echo "${myarray[$i]} ${myarray[$(( $i + 1))]}" 
done

输出:

1 2
2 3
3 4
4 4
4 5
5

虽然您的特定用例可能有更简单的解决方案,但这将允许您访问循环中数组元素的任意组合。

【讨论】:

  • arr=( $(cat foo) ) 是一种将数据加载到数组中的反模式;它分行并扩展全局。如果在 bash 4 上使用 readarray -tmapfile -t 更好,在 bash 3 上使用 IFS=$'\n' read -r -a arr &lt;fooarray=( ); while read -r; do array+=( "$REPLY" ); done 循环会更好。
  • ...另外,seq 不仅不是 bash 的一部分,它也不是 POSIX 的一部分,所以它是完全不可移植的。如果要遍历数组索引,请使用for idx in "${!array[@]}";这也修复了对稀疏数组的支持,这会导致数值迭代失败。
  • 另外,不要对不是内置变量或环境变量的变量使用全大写名称。请参阅pubs.opengroup.org/onlinepubs/009695399/basedefs/…(第四段)的 POSIX 规范中的环境变量命名约定,并记住环境变量和 shell 变量共享一个命名空间。
【解决方案2】:

此答案假定您希望您的值重叠 - 这意味着作为 next 给出的值然后在以下迭代中变为 curr

假设您将代码封装在一个函数中,该函数在下一项存在时采​​用两个参数(当前和下一个),或者在最后一项上时采用一个参数:

# a "higher-order function"; it takes another function as its argument
# and calls that argument with current/next input pairs.
invokeWithNext() {
  local funcName=$1
  local curr next

  read -r curr
  while read -r next; do
    "$funcName" "$curr" "$next"
    curr=$next
  done
  "$funcName" "$curr"
}

# replace this with your own logic
yourProcess() {
  local curr=$1 next=$2
  if (( $# > 1 )); then
    printf 'Current value is %q, and next item is %q\n' "$curr" "$next"
  else
    printf 'Current value is %q; no next item exists\n' "$curr"
  fi
}

这些定义完成了,你可以运行了:

invokeWithNext yourProcess <list.txt

...产生输出,例如:

Current value is 1, and next item is 2
Current value is 2, and next item is 3
Current value is 3, and next item is 4
Current value is 4, and next item is 5
Current value is 5; no next item exists

【讨论】:

  • (旁白:以前的版本更像是一个传统的减速器,具有可以传入的指定初始值;在重新考虑规范时,我删除了该功能,但任何好奇的人都可以看看编辑历史)。
【解决方案3】:
$ printf '%d\n' {0..10} | paste - -
0   1
2   3
4   5
6   7
8   9
10  

因此,如果您只想插入行以便每行读取两个变量...

while read -r odd even; do
    …
done < <(paste - - < inputfile)

如果您的行包含空格,您将需要做额外的工作。

【讨论】:

  • 我读到的问题是期待0 11 22 3,而不是分成对——如果“下一个”项目永远不会成为“当前”项目,那么它就是几乎没有“下一个”,是吗?
  • @CharlesDuffy SO 当问题需要解释时更有趣! -_- 无论如何,我觉得奥洛夫的回答走上了更好的轨道。
【解决方案4】:

我会将 for 循环替换为 while read xx 循环。 类似于

cat list.txt | while read line; do
  if read nextline; then
     # You have $line and $nextline
  else
     # You have garbage in $nextline and the last line of list.txt in $line
  fi
done

【讨论】:

  • 问题是我们是要在1 23 4等上运行,还是1 22 33 4等;我对这个问题有后一种解释,但这个脚本是前者。
  • 我会将cat list.txt | while read 替换为done &lt; list.txt
  • @TomFenech:是的,这样更好,节省一个 fork/exec。
  • @CharlesDuffy:再次阅读问题,我认为您的解释是正确的:-)
  • 嘿。显然不是,OP 真正想要的确实是以非重叠对的形式阅读。 :)
猜你喜欢
  • 2012-11-19
  • 1970-01-01
  • 2016-12-11
  • 1970-01-01
  • 1970-01-01
  • 2018-05-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多