다 변수 For 루프


12

for루프에서 정수뿐만 아니라 여러 변수를 지정하는 방법이 bash있습니까? 작업해야 할 임의의 텍스트가 포함 된 2 개의 파일이있을 수 있습니다.

기능적으로 필요한 것은 다음과 같습니다.

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

어떤 아이디어?

답변:


11

먼저 단어 분리를 통해 줄을 읽는 데 피할 수없는 몇 가지 문제가 있으므로 for로 줄을 읽지 마십시오 .

길이가 같은 파일을 가정하거나 두 파일 중 더 짧은 파일을 읽을 때까지만 반복하려면 간단한 해결책이 가능합니다.

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

readfalse를 반환 할 때 와 몇 가지 다른 이유로 더 일반적인 솔루션을 구성하는 것은 어렵습니다 . 이 예제는 임의의 수의 스트림을 읽고 최단 또는 최장 입력 후 리턴 할 수 있습니다.

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


||나를 위해 작동하지 않습니다. file1 처리 한 다음 file2를 처리 하지만 파일을 동기화 된 상태로 유지 하여 첫 번째 eof&& 에서 while 루프 를 종료합니다 .
-GNU

테스트 할 자유 시간이 없었습니다. 예 : 반복마다 두 요소를 원할 것입니다. ||"목록"연산자는 대부분의 언어처럼 단락된다. 확실히 이것은 전에 천 번 대답되었습니다 ...
ormaaj

요점은 이전에 무언가가 자주 언급 된 빈도가 아닙니다. 오히려 현재 답변에 표시되는 코드 가 작동하지 않는 것 입니다. " 아마도 두 요소를 모두 원합니다."에 따라 테스트하지 않은 것 같습니다.
Peter.O

@ Peter.O 알고 있습니다. 수정되었습니다.
ormaaj

2

해야 할 일; 완료-두 개의 for와 두 개의 완료가 필요합니다.

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

2 개의 파일을 추가하고 중첩하지 않으려면 다음을 수행하십시오.

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

2 개의 파일을 압축하려면 paste를 사용하십시오.

paste file1 file2

1
당신은 관련된 서브 프로세스가 없다고 말합니다. ()가 서브 쉘이 아닙니까? 나는 펜 던 트되고 싶지 않지만 지금 무슨 일이 일어나고 있는지 잘 모르겠습니다. 예를 들어 스크립트가 있고 cd를 사용하여 현재 쉘을 오염시키지 않으려는 경우 () 내부에서 수행한다는 것을 알고 있습니다. 또한 실행 중 (잠자기 4) & 다음에 ps가 표시되면 프로세스가 여전히 실행 중임을 나타냅니다. 자세히 설명해 주시겠습니까?
Silverrocker 2016 년

글쎄요, 미안 해요 나는 그렇게 들었다고 생각했지만 어쩌면 그것은와 <비교 되었을뿐입니다 cat. 테스트 방법과 의미 론적 시점이 확실하지 않습니다. 파이프가 호출되면 하위 프로세스의 변수는 기본 프로세스까지 버블 링되지 않지만 여기에는 변수가 없습니다. 누군가가 그것을 명확히 할 수있을 때까지 나는 비판적인 문장을칩니다.
사용자 알 수 없음

0

whileinteresitng 구문이 있습니다. do ... while루프 앞에 여러 명령을 넣을 수 있으며 문제의 경우 특정 요구 사항에 따라이 기능을 조정해야 할 수도 있습니다.

예를 들어, 질문의 요구 사항에 따라 read || read단순히 작동하지 않습니다 . 첫 번째 파일을 true읽은 경우 첫 번째 파일을 처음부터 끝까지 읽을 때까지 두 번째 파일을 건너 뜁니다 . 그러면 상태 때문에 는 여전히 truewhile 루프가 계속되고 두 번째 파일을 처음부터 끝까지 읽습니다.

read && read가장 짧은 파일까지만 읽으려는 경우 파일을 동시에 (동기식으로) 읽습니다. 그러나 두 파일을 모두 읽으려면 구문 요구 사항 eof을 사용해야합니다 while's. 명령에 의해 바로 전에do while 0이 아닌 리턴 코드를 생성하는 루프는 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를 테스트하고 싶을 수도 있지만 일반적인 최종 아이디어는 특히 최종 참 / 거짓 조건에 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.