Bourne Shell에서 여러 줄 읽기

Bourne Shell에서 여러 줄 읽기

두 줄을 두 개의 변수로 읽으려고합니다. Bash에서는 다음과 같은 것을 사용합니다.

cat << EOF > myfile
line1
line2
EOF

cat myfile |  {
 read firstline
 echo $firstline # "line1" in bash and sh
 read secondline
}

echo $firstline # "line1" in bash, empty in sh
echo $secondline

그러나 Bourne Shell에서는 $firstline$secondline명령 그룹 외부에서는 비어 있습니다. sh에서 어떻게 할 수 있나요?

답변1

firstline(테스트에서 코드를 테스트할 때 이를 설정했을 가능성이 높으며 bash해당 값은 결국 비어 있어야 합니다.)

파이프라인을 실행할 때

cat myfile | { read firstline; read secondline; }

오른쪽은 서브쉘에서 실행됩니다. 때문이 아니라 { ...; }파이프라인의 일부이기 때문입니다. 서브셸 환경에는 두 개의 변수 firstlinesum secondline(둘 다 읽은 후)이 포함되지만 서브셸이 종료되면 두 변수가 모두 삭제되어 삭제됩니다.

이는 POSIX shbash.

(4.2+) 에서는 bash쉘 옵션을 설정하여 이 문제를 해결할 수 있습니다 lastpipe. bash매뉴얼 에서 :

파이프라인의 각 명령은 별도의 프로세스(즉, 하위 셸에서)로 실행됩니다. 서브셸 환경에 대한 설명은 명령 실행 환경을 참조하세요. lastpipe내장 옵션을 사용하여 활성화 하면 shopt(아래 설명 참조 shopt) 파이프의 마지막 요소가 쉘 프로세스에 의해 실행될 수 있습니다.

이는 스크립트에서는 작동하지만 작업 제어가 활성화된 대화형 셸에서는 작동하지 않습니다(작업 제어로 인해 작동하지 않고 대화형이 아님).

예:

$ cat script.sh
cat << EOF > myfile
line1
line2
EOF

cat myfile | { read firstline; read secondline; }
printf 'first=%s\n' "$firstline"
printf 'second=%s\n' "$secondline"

shopt -s lastpipe
cat myfile | { read firstline; read secondline; }
printf 'first=%s\n' "$firstline"
printf 'second=%s\n' "$secondline"
$ bash script.sh
first=
second=
first=line1
second=line2

특정 경우에는 POSIX에서 완전히 취소 하고 파이프 sh하는 대신 두 호출을 직접 사용하여 복합 명령으로 리디렉션할 수도 있습니다.bashcatread

{ read firstline; read secondline; } <myfile

그건 그렇고, 당신의 컴퓨터에는 실제 역사적인 Bourne 쉘이 없을 가능성이 높습니다(Solaris 11 이전 Solaris 시스템이 아닌 한). 나는 당신이 최신 POSIX sh쉘을 언급하고 있다고 가정합니다.

답변2

파이프의 오른쪽을 사용하는 경우 몇 가지 제한 사항이 있습니다.

사용하기 전에 변수가 설정되지 않았는지 확인하기 위해 코드를 변경하고 약간 압축했습니다.

#! /bin/sh

unset firstline secondline
printf '%s\n%s\n' line1 line2 > myfile

cat myfile | { read firstline; read secondline
               echo "internal 1: $firstline"     # "line1" in most shells
               echo "internal 2: $secondline"    # "line2" in most shells
             }

echo "Outside 1: $firstline"     # "line1" in ksh, zsh (even called as sh).
                                 #  Empty in sh, bash, mksh, dash, ash, yash.
echo "Outside 2: $secondline"    # "line2" in ksh, zsh.
                                 # Same as above for other shells.

복합 명령의 내부 부분에는 {...}값이 있고, 외부 부분( Outside)은 비어 있는 경우가 많습니다(특히 POSIX, 이식 가능한 경우).

shopt -s lastpipeBash는 파이프 기호 오른쪽에 값을 설정한 경우에만 값을 유지합니다. ksh(93)와 zsh는 모두 기본 읽기 값을 유지합니다.

따라서 문제는 파이프 |표기법을 사용하지 않는 것입니다. 음, 이 경우에는 매우 간단합니다. 또는 변수 값을 이식 가능하게 유지하도록 설정하고 있습니다 <file { ... }.{...} <file

#! /bin/sh

unset firstline secondline
printf '%s\n%s\n' line1 line2 > myfile

{ read firstline; read secondline
  printf '%s\n' "internal 1: $firstline"     # "line1" in most shells
  printf '%s\n' "internal 2: $secondline"    # "line2" in most shells
} < myfile

printf '%s\n' "Outside 1: $firstline"          # "line1" in most shells.
printf '%s\n' "Outside 2: $secondline"         # "line2" in most shells.

이 작업 세트는 이전 Bourne 쉘을 제외한 모든 쉘에서 테스트되었습니다(현재 쉘에서 실행하기 위한 {...} 구성이 부족하여 해결 방법이 더 어려워지고 시간을 들일 가치가 없습니다. @schily 답변을 읽어보십시오).

답변3

readBourne Shell다음 두 가지 제한 사항으로 인해 Bourne Shell 에서는 이 작업을 사용할 수 없습니다 .

  • 파이프의 오른쪽은 항상 하위 쉘에서 실행됩니다.

    사용하면 명령 echo foo | read VARread항상 하위 쉘에서 실행됩니다.

  • Bourne Shell은 쉘의 내장 명령이 포함된 명령 목록을 { ...; }서브쉘에 넣습니다. 그래서

    { read A; read B; } < file서브쉘에서도 실행됩니다.

이를 확인하고 싶다면 이식성이 뛰어난 Solaris Bourne Shell 버전으로 obosh확인하는 것이 좋습니다.schilytools

바라보다:http://sourceforge.net/projects/schilytools/files/

최신 쉘( ksh93이것이 내장 쉘인 경우 프로세스 내 메인 쉘 파이프의 맨 오른쪽에 있는 프로그램을 실행하여 시작하십시오. 따라서 bosh이는 Bourne 쉘의 최신 버전을 사용할 때 유용할 것입니다( ) bosh서브쉘도 생성되지 않습니다.) { ...; }성능상의 이유로 부작용이 있을 수 있습니다.

{ read A; read B; } < file

그리고bosh

다음은 모든 셸에서 작동합니다.

exec 3<&0               # save standard input as fd 3
exec < file             # use file as stdin
read A
read B
exec 0<&3               # restore standard input
exec 3<&-               # close file descriptor 3

관련 정보