쉘에서 입력을 한 줄씩(빈 줄 포함) 처리합니다.

쉘에서 입력을 한 줄씩(빈 줄 포함) 처리합니다.

쉘 스크립트에서는 명령줄의 출력을 한 줄씩 구문 분석해야 합니다. 출력에는 빈 줄이 포함될 수 있으며 이는 관련이 있습니다. 나는 bash가 아닌 ash를 사용하고 있으므로 프로세스 대체에 의지할 수 없습니다. 나는 이것을 시도하고 있습니다 :

    OUT=`my_command`
    IFS=$'\n'              
    i=1
    for line in $OUT; do
        echo $line                                          
        eval VAL$i=$line             
        i=$((i+1))             
    done

그러나 이는 $OUT에서 빈 줄을 삭제합니다. 빈 줄도 처리되도록 하려면 어떻게 해야 합니까?

답변1


작동하는 쉘 루프는 다음과 같습니다.

set -f -- "-$-"' -- "$@" '"
    ${IFS+IFS=\$2} ${out+out=\$3}" \
    "$IFS" "$out" "$@"
IFS='
';for out in $(my command|grep -n '.\|')
do  : something with "${out%%:*}" and "${out#*:}"
done
unset IFS out
eval "set +f $1"
shift 3

빈 줄이 없도록 정렬하면 됩니다. 처음에는 이 목적으로 제안했지만 nl다시 생각해보면 nl논리적 페이지 구분 기호가 입력에 나타나서 출력이 왜곡될 수 있습니다.(실제로는 빈 행으로 끝나고 번호가 매겨진 행에 영향을 미칩니다. 하지만 다른 목적으로는 매우 편리한 기능입니다.). 밖의아니요논리적인 페이지 나누기를 해석하면 grep -n '.\|'결과는 동일합니다.

이와 같은 파이프라인과 일부 매개변수 대체를 사용하면 빈 줄 문제를 피할 수 있을 뿐만 아니라 각 반복에 동시에 번호가 미리 지정됩니다.(현재 반복 횟수는 이제 사용자에게 제공된 각 값의 시작 부분에 있으며 $out그 뒤에는 :).

set ... IFS=...줄의 목적은 쉘의 상태가 변경 전의 위치로 복원되도록 하는 것입니다. 함수가 아닌 스크립트인 경우 이러한 예방 조치는 과잉일 수 있습니다. 그래도 해야 해적어도 set -f쉘 분할 전에 의도하지 않은 입력 오류를 피하십시오.


그러나 프로세스 대체에 (d)ash대해서는<()

그런 다음 다시 데비안에서( dash)파생됨ash (예를 들어 busybox ash)파일 설명자 연결 처리와 여기에 있는 문서가 익숙할 수 있는 프로세스 대체에 대한 <(더 나은 대안을 제공한다는 것을 알 수 있습니다 ).

다음 예를 고려하십시오.

exec "$((i=3))"<<R "$((o=4))"<<W 3<>/dev/fd/3 4<>/dev/fd/4
R
W

sed -u 's/.*/here I am./' <&"$o" >&"$i" &
echo "hey...sed?" >&"$o"
head -n1          <&"$i"

왜냐면 파생 상품이 여기에 있기 때문입니다 dash. 대신 익명 파이프를 사용한 문서입니다.(대부분의 다른 껍질과 마찬가지로)일반 파일과 함께 사용하고 /dev/fd/[num]Linux 시스템에서의 링크는 파일 설명자의 백업 파일을 참조하는 간접적인 방법을 제공하기 때문입니다.(파일 시스템에서 참조할 수 없는 경우에도 - 예: 익명 파이프)위의 순서는 호출될 수 있는 일부 쉘을 설정하는 매우 간단한 방법을 보여줍니다.공동 프로세스. 예를 들어 Linux 시스템에서 busybox ash또는 Linux 시스템에서dash(다른 사람에 대해서는 보증하지 않습니다)위 내용은 다음과 같이 인쇄됩니다.

here I am.

$i...그리고 쉘이 파일 설명자를 닫을 때까지 계속 그렇게 할 것입니다 $o. 버퍼링 문제를 피하기 위해 -uGNU 에서 제공하는 nbuffered 스위치를 사용 하지만, 스위치가 없어도 필요한 경우 백그라운드 프로세스에 대한 입력을 필터링하고 sed파이프의 바이트 단위로 시간을 측정할 수 있습니다.conv=sync\0NULdd

sed대화형 셸에서 위의 내용을 일반적으로 사용하는 방법은 다음과 같습니다.

: & SEDD=$$$!
sed -un "/^$SEDD$/!H;//!d;s///;x;/\n/!q;s///;s/%/&&/g;l" <&"$o" >&"$i" &

...그 배경 a는 sed고유한 구분 기호가 나타날 때까지 입력을 읽고 저장합니다. 이 시점에서 이전 버퍼 %에 있던 내용을 두 배로 늘리고 H익명 파이프 exec형식 친화적인 C에서 단일 문자열의 정의 문자열로 printf를 인쇄합니다. line - 또는 결과가 80자를 초과하는 경우 여러 줄에 표시됩니다. 마지막 항목(GNU의 경우 sed)은 w/ 로 처리할 수 있습니다. 이 스위치는 절대로 줄 바꿈을 하지 않도록 sed -l0지시하는 스위치입니다 . 또는 다음과 같습니다.sed\

fmt=
while IFS= read -r r <&"$i" 
      case $r in (*$) 
      ! fmt=$fmt$r ;;esac
do    fmt=$fmt${r%?} 
done

어쨌든, 나는 다음과 같이 버퍼를 구축했습니다.

echo something at sed >&"$o"
printf '%s\n' more '\lines%' at sed "$SEDD" >&"$o"

그리고 그것을 잡아당기면 다음과 같습니다...

IFS= read -r fmt <&"$i"

다음에 일어나는 일은 다음 과 같습니다 $fmt.

printf %s\\n "$fmt"
something at sed\nmore\n\\lines%%\nat\nsed$

sed인쇄할 수 없는 문자에 대한 C 스타일 8진수 이스케이프도 수행됩니다.

그래서 나는 그것을 다음과 같이 사용할 수 있습니다 ...

printf "%d\n${fmt%$}\n" 1 2 3

...인쇄...

1
something at sed
more
\lines%
at
sed
2
something at sed
more
\lines%
at
sed
3
something at sed
more
\lines%
at
sed

sed예를 들어 필요에 따라 파이프를 종료하고 해제 할 수 있습니다 .

printf %s\\n "$SEDD" "$SEDD" >&"$o"
exec "$i">&- "$o">&-

fd를 한 번만 사용하는 대신 fd를 보유하면 이런 종류의 작업을 수행할 수 있습니다. 필요한 만큼 백파이프를 유지 관리할 수 있으며, 커널이 해당 링크를 소유한 프로세스를 제외한 어떤 프로세스에도 이러한 링크를 제공하지 않기 때문에 명명된 파이프보다 더 안전합니다.(당신의 껍질), 명명된 파이프는 찾을 수 있지만(도청/도둑질도 포함)참조된 파일에 액세스할 수 있는 모든 프로세스는 파일 시스템에서 이 작업을 수행할 수 있습니다.

프로세스 교체를 수행하는 쉘에서 비슷한 작업을 수행하려면 아마도 다음과 같이 할 수 있습니다.

eval "exec [num]<>"<(:)

...하지만 시도해 본 적은 없습니다.

답변2

이 방법:

i=1
my_command | while read line; do
    echo $line
    eval VAL$i="$line"
    i=$((i+1))
done

명령의 출력은 한 줄씩 읽히므로 먼저 변수에 저장할 필요 없이 줄(빈 줄 포함)이 개별적으로 처리됩니다. 출력이 메모리에 두 번 저장되지 않고 bash 스크립트는 명령이 완료된 직후가 아니라 출력 직후에 줄 처리를 시작할 수 있으므로 메모리도 절약됩니다.

편집: VALx 변수는 위의 하위 셸에 설정되어 있으므로 수정해야 합니다.

eval `i=1
my_command | while read line; do
    # echo $line
    echo "VAL$i=\"$line\""
    i=$((i+1))
done`

정말로 필요한 경우에도 echo $line몇 가지 수정이 필요합니다.

답변3

여기 문서를 사용하여 이를 구현했습니다.

    i=1
    while read -r line; do
        eval VAL$i=\$line
        i=$((i+1))
    done <<EOF
$(my_command)
EOF

좋은 결과.

업데이트됨: Gilles 및 mikeserv의 피드백이 통합되었습니다.

관련 정보