多变量 For 循环

多变量 For 循环

有没有办法在for循环中指定多个变量(不仅仅是整数) bash?我可能有 2 个包含我需要处理的任意文本的文件。

我的功能需要是这样的:

for i in $(cat file1) and j in $(cat file2); do command $i $j; done

有任何想法吗?

答案1

第一的,不要用 for 读取行,因为通过分词读取行存在几个不可避免的问题。

假设文件长度相等,或者如果您只想循环直到读取两个文件中较短的一个,则可以使用简单的解决方案。

while read -r x && read -r y <&3; do
    ...
done <file1 3<file2

由于何时read返回 false 和其他几个原因,组合一个更通用的解决方案很困难。此示例可以读取任意数量的流,并在最短或最长的输入后返回。

#!/usr/bin/env bash

# Open the given files and assign the resulting FDs to arrName.
# openFDs arrname file1 [file2 ...]
openFDs() {
    local x y i arr=$1

    [[ -v $arr ]] || return 1
    shift

    for x; do
        { exec {y}<"$x"; } 2>/dev/null || return 1
        printf -v "${arr}[i++]" %d "$y"
    done
}

# closeFDs FD1 [FD2 ...]
closeFDs() {
    local x
    for x; do
        exec {x}<&-
    done
}

# Read one line from each of the given FDs and assign the output to arrName.
# If the first argument is -l, returns false only when all FDs reach EOF.
# readN [ -l ] arrName FD1 [FD2 ...]
readN() {
    if [[ $1 == -l ]]; then
        local longest
        shift
    else
        local longest=
    fi

    local i x y status arr=$1
    [[ -v $arr ]] || return 1
    shift

    for x; do
        if IFS= read -ru "$x" "${arr}[i]" || { unset -v "${arr}[i]"; [[ ${longest+_} ]] && return 1; }; then
            status=0
        fi
        ((i++))
    done
    return ${status:-1}
}

# readLines file1 [file2 ...]
readLines() {
    local -a fds lines
    trap 'closeFDs "${fds[@]}"' RETURN
    openFDs fds "$@" || return 1

    while readN -l lines "${fds[@]}"; do
        printf '%-1s ' "${lines[@]}"
        echo
    done
}

{
    readLines /dev/fd/{3..6} || { echo 'error occured' >&2; exit 1; }
} <<<$'a\nb\nc\nd' 3<&0 <<<$'1\n2\n3\n4\n5' 4<&0 <<<$'x\ny\nz' 5<&0 <<<$'7\n8\n9\n10\n11\n12' 6<&0

# vim: set fenc=utf-8 ff=unix ts=4 sts=4 sw=4 ft=sh nowrap et:

因此,根据是否readNgets -l,输出是

a 1 x 7
b 2 y 8
c 3 z 9
d 4 10
5 11
12

或者

a 1 x 7
b 2 y 8
c 3 z 9

必须循环读取多个流而不将所有内容保存到多个数组中的情况并不常见。如果你只想读取数组,你应该看看mapfile.

答案2

完成;完成;完成 - 你需要两个fors和两个done:

for i in $(< file1); do for j in $(< file2); do echo $i $j;done ; done

当然,文件 2 中的每个单词都会被处理一次。

在大多数情况下,使用 < 代替 cat 应该不会带来显着的性能提升。不涉及子流程。(不确定最后一句话)。

未压缩:

for i in $(< file1)
do
  for j in $(< file2)
  do
    echo $i $j
  done
done

如果您不喜欢阅读单词,而是阅读行,并保留空格,请使用 while 并阅读:

while read line; do while read second; do echo "${line}${second}"; done <file2 ; done <file1

如果你想追加 2 个文件,而不是嵌套它们:

cat file1 file2 | while read line; do echo "${line}"; done

如果您想压缩 2 个文件,请使用粘贴:

paste file1 file2

答案3

while有一个有趣的语法。您可以在do ... while循环之前放置多个命令,并且所讨论的情况可能需要兼顾此功能,具体取决于您的特定要求:您是读到最长文件的末尾,还是只读到最短文件的末尾。

例如,read || read简单地不起作用(根据问题的要求),因为当第一个文件的读取为 时true,第二个文件的读取为跳过直到第一个文件从头到尾读取完毕...然后,由于状态为 still true,因此 while 循环继续并从头到尾读取第二个文件。

read && read如果您只想读取最短的文件,则会同时(同步)读取文件。但是,如果您想将这两个文件读取到eof,那么您需要满足while's语法要求,即。立即通过命令循环do while产生非零返回代码以跳出 while 循环。

这是如何读取两个文件的示例埃夫

while IFS= read -r line3 <&3 || ((eof3=1))
      IFS= read -r line4 <&4 || ((eof4=1))
      !((eof3 & eof4))
do
    echo "$line3, $line4"
done 3<file3 4<file4

(您可能想在读取之前测试 eof3 和 eof4,但总体思路是存在的,尤其是在最终的真/假条件下。

相关内容