多变量循环


12

有没有一种方法可以在for循环中指定多个变量(不仅仅是整数)bash?我可能有2个文件,其中包含我需要使用的任意文本。

我在功能上需要的是这样的:

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

有任何想法吗?

Answers:


11

首先,不要读取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:

因此,取决于是否readN获取-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


||对我不起作用。它先处理file1 然后再处理 file2,但确实使文件保持同步&&,并在第一次eof时退出while循环。
-GNU

我没有空闲时间来测试-是的,您可能希望每次迭代都使用这两个元素。该||“目录”操作符短路像大多数语言。当然,这已经被回答了一千遍了……
ormaaj 2012年

关键不是以前提到某事的频率。相反,这是您当前在答案显示的代码不起作用。根据您的“ 可能想要两个元素..”,听起来您还没有测试它。
Peter.O 2012年

@ Peter.O我知道。已修复。
ormaaj 2012年

2

做完 完成-您需要两个要点和两个要点:

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

当然,file2对file1中的每个单词处理一次。

在大多数情况下,使用<代替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

如果要附加两个文件,而不是嵌套它们:

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

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

paste file1 file2

1
您说没有涉及的子流程。()不是子shell吗?我不想被悬赏,但是我不确定现在正在发生什么。例如,我知道当我有一个脚本并且不想让它用cd污染当前的外壳时,我会在()中进行操作。同样正在运行(睡眠4)&,例如后面跟着ps,则表明进程仍在运行。您能详细说明一下吗?
Silverrocker 2012年

好吧-对不起,我在这里没有基础就表现得非常好。我以为我听到了,但也许只是与<相比cat。而且我不确定如何测试它,以及何时将在语义上变得重要。如果调用管道,则子流程中的变量不会冒泡至主流程,但此处没有变量。我严厉批评了句子,直到有人可以澄清为止。
用户未知

0

while具有Interesitng语法。您可以在do ... while循环之前放置多个命令,有问题的情况可能需要使用此功能,具体取决于您的以下特定要求:是读取到最长文件的末尾,还是只读取最短文件的末尾。

例如,read || read根本不起作用(根据问题的要求),例如,当第一个文件的读取为时true,第二个文件的读取将被跳过,直到从头到尾读取第一个文件为止...然后,因为状态为仍然是true,while循环继续并从头到尾读取第二个文件。

read && read如果您只想读取最短的文件,它将同时(同步)读取文件。但是,如果您想将两个文件都读取到eof,则需要满足while's语法要求,即。由命令立即之前do while产生非零返回代码循环打破while循环的进行。

这是一个如何将两个文件读到eof的示例

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,但是总的思路就存在了,尤其是在最终的true / false条件下。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.