Đa biến cho các vòng lặp


12

Có cách nào để chỉ định nhiều biến (không chỉ số nguyên) trong forcác vòng lặp bashkhông? Tôi có thể có 2 tệp chứa văn bản tùy ý mà tôi sẽ cần phải làm việc với.

Những gì tôi cần chức năng là một cái gì đó như thế này:

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

Có ý kiến ​​gì không?

Câu trả lời:


11

Đầu tiên, đừng đọc các dòng với , vì có một số vấn đề không thể tránh khỏi với việc đọc các dòng thông qua việc tách từ.

Giả sử các tệp có độ dài bằng nhau hoặc nếu bạn chỉ muốn lặp cho đến khi hai tệp ngắn hơn được đọc, một giải pháp đơn giản là có thể.

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

Kết hợp một giải pháp tổng quát hơn là khó vì khi readtrả về sai và một số lý do khác. Ví dụ này có thể đọc số lượng luồng tùy ý và trả về sau đầu vào ngắn nhất hoặc dài nhất.

#!/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:

Vì vậy, tùy thuộc vào việc có readNđược -l, đầu ra là một trong hai

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

hoặc là

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

Phải đọc nhiều luồng trong một vòng lặp mà không lưu mọi thứ vào nhiều mảng không phải là tất cả. Nếu bạn chỉ muốn đọc mảng, bạn nên xem qua mapfile.


||không làm việc cho tôi. Nó xử lý file1 rồi file2 , nhưng nó giữ các tập tin đồng bộ với &&, mà sẽ thoát khỏi trong khi vòng lặp khi đầu eof . - GNU bash 4.1.5
Peter.O

Tôi không có thời gian rảnh để kiểm tra - vâng, có lẽ bạn sẽ muốn cả hai yếu tố trên mỗi lần lặp. Các ||"danh sách" nhà điều hành được ngắn mạch như trong hầu hết các ngôn ngữ. Chắc chắn điều này đã được trả lời hàng ngàn lần trước khi ...
ormaaj

Vấn đề không phải là tần suất của một cái gì đó thường được đề cập trước đó. Thay vào đó, đó là mã bạn hiện đang hiển thị trong câu trả lời của bạn không hoạt động . Dựa trên " có thể muốn cả hai yếu tố .." của bạn, có vẻ như bạn vẫn chưa kiểm tra nó.
Peter.O

@ Peter.O Tôi biết. Nó đã được sửa.
ormaaj

2

Làm xong; xong - bạn cần hai fors và hai dones:

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

File2, tất nhiên, được xử lý cho mỗi từ trong file1 một lần.

Sử dụng <thay vì mèo sẽ là một hiệu suất đạt được không đáng kể trong hầu hết các trường hợp. Không có quy trình phụ liên quan. (Không chắc chắn về câu cuối cùng).

Không nén

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

Nếu bạn không thích đọc các từ, nhưng các dòng và giữ khoảng trắng, hãy sử dụng while và đọc:

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

Nếu bạn muốn nối thêm 2 tệp và không lồng chúng:

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

Nếu bạn muốn nén 2 tệp, hãy sử dụng dán:

paste file1 file2

1
Bạn nói không có quy trình phụ liên quan. Không phải là () một subshell? Tôi không muốn bị treo cổ nhưng tôi không chắc chuyện gì đang xảy ra. Chẳng hạn, tôi biết rằng khi tôi có một tập lệnh và tôi không muốn nó xử lý trình bao hiện tại với cd, tôi thực hiện nó bên trong một (). Cũng đang chạy (ngủ 4) & ví dụ theo sau là ps cho tôi thấy quá trình vẫn đang chạy. Bạn có thể vui lòng giải thích.
Silverrocker

Chà - tôi xin lỗi, tôi diễn rất nhiều mà không có nguyên tắc cơ bản ở đây. Tôi nghĩ rằng tôi đã nghe như vậy, nhưng có lẽ nó chỉ là <so sánh với cat. Và tôi không chắc làm thế nào để kiểm tra nó, và khi nào nó sẽ trở nên quan trọng về mặt ngữ nghĩa. Nếu các đường ống được gọi, các biến trong các quy trình con không nổi lên theo quy trình chính, nhưng chúng ta không có các biến ở đây. Tôi vượt qua câu quan trọng, cho đến khi ai đó có thể làm rõ nó.
người dùng không xác định

0

whilecó một cú pháp interesitng. Bạn có thể đặt nhiều lệnh trước do ... whilevòng lặp và trường hợp được đề cập có thể cần phải xử lý tính năng này, tùy thuộc vào yêu cầu cụ thể của bạn về: bạn có đọc đến hết tệp dài nhất hay chỉ đến cuối tệp ngắn nhất.

Ví dụ, read || readđơn giản là không hoạt động (theo yêu cầu của câu hỏi), vì khi đọc tệp đầu tiên true, tệp thứ hai sẽ bị bỏ qua cho đến khi tệp đầu tiên được đọc từ đầu đến cuối ... Sau đó, vì trạng thái vẫn còn true, vòng lặp while tiếp tục và đọc tệp thứ hai từ đầu đến cuối.

read && readsẽ đọc các tệp đồng thời (đồng bộ) nếu bạn chỉ muốn đọc xa nhất là tệp ngắn nhất. Tuy nhiên, nếu bạn muốn đọc cả hai tệp eof, thì bạn cần phải làm việc với while'scác yêu cầu cú pháp, tức là. bởi lệnh ngay lập tức trước khi các do whilevòng lặp tạo ra một mã trở lại khác không để thoát ra khỏi vòng lặp while.

Dưới đây là một ví dụ về cách đọc cả hai tệp vào 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

(bạn có thể muốn kiểm tra eof3 và eof4 trước khi đọc, nhưng ý tưởng chung là có, đặc biệt là trong điều kiện đúng / sai cuối cùng.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.