如何正确嵌套循环

时间:2016-06-20 03:43:26

标签: bash

我有2个脚本,#1和#2。每个工作都可以自己做。我想逐行读取15行文件并进行处理。脚本#2选择行。行0表示为firstline = 0,lastline = 1。第14行是firstline = 14,lastline = 15。我看到echo的结果很好。我想用脚本#1做同样的事情。无法正确嵌套。代码如下。

#!/bin/bash

    # script 1

    filename=slash
    firstline=0
    lastline=1

    i=0
    exec <${filename}
    while read ; do
      i=$(( $i + 1 ))
      if [ "$i" -ge "${firstline}" ] ; then
        if [ "$i" -gt "${lastline}" ] ; then
          break
        else
          echo "${REPLY}" > slash1
          fold -w 21 -s slash1 > news1
          sleep 5
        fi
      fi
    done


    # script2

    firstline=(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14)
    lastline=(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)

    for ((i=0;i<${#firstline[@]};i++))
    do
      echo ${firstline[$i]} ${lastline[$i]};
    done

2 个答案:

答案 0 :(得分:1)

你的问题很不清楚,但也许你只是在寻找一些简单的函数调用:

#!/bin/bash

script_1() {

    filename=slash
    firstline=$1
    lastline=$2

    i=0
    exec <${filename}
    while read ; do
      i=$(( $i + 1 ))
      if [ "$i" -ge "${firstline}" ] ; then
        if [ "$i" -gt "${lastline}" ] ; then
          break
        else
          echo "${REPLY}" > slash1
          fold -w 21 -s slash1 > news1
          sleep 5
        fi
      fi
    done
}


    # script2

    firstline=(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14)
    lastline=(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)

    for ((i=0;i<${#firstline[@]};i++))
    do
      script_1 ${firstline[$i]} ${lastline[$i]};
    done

请注意,以这种方式读取文件效率非常低,并且毫无疑问有更好的方法来处理这个问题,但我正在尝试最小化代码中的更改。

答案 1 :(得分:1)

更新:根据您之后的评论,使用sed提取每次迭代中感兴趣的行的以下惯用Bash代码可以更加简单地解决您的问题:

注意:
- 如果输入文件在循环迭代之间没有变化,并且输入文件足够小(就像在手边的情况下那样),那么预先在变量中缓冲文件内容会更有效,因为在下面的原始答案中证明 - 正如tripleee在评论中指出:如果只是按顺序读取输入行 就足够了(而不是通过特定行号提取行,那么单个,简单while read -r line; do ... # fold and output, then sleep ... done < "$filename"就足够了。 功能

# Determine the input filename.
filename='slash'

# Count its number of lines.
lineCount=$(wc -l < "$filename")

# Loop over the line numbers of the file.
for (( lineNum = 1; lineNum <= lineCount; ++lineNum )); do
  # Use `sed` to extract the line with the line number at hand,
  # reformat it, and output to the target file. 
  fold -w 21 -s <(sed -n "$lineNum {p;q;}" "$filename") > 'news1'
  sleep 5
done

想要您尝试实现的内容的简化版本:

#!/bin/bash

# Split fields by newlines on input,
# and separate array items by newlines on output.
IFS=$'\n'

# Read all input lines up front, into array ${lines[@]}
# In terms of your code, you'd use
#   read -d '' -ra lines < "$filename"
read -d '' -ra lines <<<$'line 1\nline 2\nline 3\nline 4\nline 5\nline 6\nline 7\nline 8\nline 9\nline 10\nline 11\nline 12\nline 13\nline 14\nline 15'

# Define the arrays specifying the line ranges to select. 
firstline=(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14)
lastline=(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)

# Loop over the ranges and select a range of lines in each iteration.
for ((i=0; i<${#firstline[@]}; i++)); do
    extractedLines="${lines[*]: ${firstline[i]}: 1 + ${lastline[i]} - ${firstline[i]}}"
    # Process the extracted lines.
    # In terms of your code, the `> slash1` and `fold ...` commands would go here.
    echo "$extractedLines" 
    echo '------'
done

注意:

  • 填充read -ra的数组变量的名称为lines; ${lines[@]}是用于将所有数组元素作为单独的单词返回的Bash语法(${lines[*]}也引用所有元素,但语义略有不同),并且在语句中使用此语法说明lines确实是一个数组变量(请注意,如果您只使用$lines来引用该变量,则您只能隐式获取该项目index 0,与:${lines[0]}相同。

  • <<<$'line 1\n...'使用here-string<<<)阅读ad-hoc 示例文档(表示为ANSI C-quoted string$'...'))为了使我的示例代码自包含

    • 正如评论中所述,您只需从$filename读取:
      read -d '' -ra lines <"$filename"
  • extractedLines="${lines[*]: ${firstline[i]}: 1 + ${lastline[i]} - ${firstline[i]}}"提取感兴趣的线条; ${firstline[i]}引用数组i中的当前元素(索引${firstline[@]});自Bash的阵列切片语法中的最后一个标记
    ${lines[*]: <startIndex>: <elementCount>})是要返回的元素的 count ,我们必须执行计算来确定计数,这是1 + ${lastline[i]} - ${firstline[i]}所做的。< / p>

    • 通过使用"${lines[*]...}"而不是"${lines[@]...}",提取的数组元素由$IFS中的第一个字符连接,在我们的示例中为新行($'\n' )(当提取单个行时,这并不重要)。