bash 파일 리디렉션은 Linux의 표준 셸(`sh`)과 어떻게 다릅니까?

bash 파일 리디렉션은 Linux의 표준 셸(`sh`)과 어떻게 다릅니까?

런타임에 사용자를 전환하는 스크립트를 작성하고 파일 리디렉션을 stdin으로 사용하여 실행했습니다. 그래서 user-switch.sh......

#!/bin/bash

whoami
sudo su -l root
whoami

이것을 실행하면 bash내가 기대하는 동작이 나타납니다.

$ bash < user-switch.sh
vagrant
root

sh그러나 다음을 사용하여 스크립트를 실행하면

$ sh < user-switch.sh 
vagrant
vagrant

bash < user-switch.sh주어진 출력이 와 다른 이유는 무엇입니까 sh < user-switch.sh?

노트:

  • Debian Jessie를 실행하는 두 개의 다른 컴퓨터에서 발생했습니다.

답변1

유사한 스크립트, no sudo, 그러나 유사한 결과:

$ cat script.sh
#!/bin/bash
sed -e 's/^/--/'
whoami

$ bash < script.sh
--whoami

$ dash < script.sh
itvirta

사용 하면 bash스크립트의 나머지 부분이 입력으로 사용되어 쉘에 의해 해석됩니다 .seddash

strace다음에서 실행: dash스크립트 덩어리(여기서는 전체 스크립트를 담기에 충분한 8kB)를 읽고 다음을 생성합니다 sed.

read(0, "#!/bin/bash\nsed -e 's/^/--/'\nwho"..., 8192) = 36
stat("/bin/sed", {st_mode=S_IFREG|0755, st_size=73416, ...}) = 0
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|...

이는 파일 핸들이 파일 끝에 있고 sed입력이 표시되지 않음을 의미합니다. 나머지는 에 버퍼링됩니다 dash. (스크립트 길이가 블록 크기 8kB를 초과하면 나머지는 읽혀집니다 sed.)

반면 Bash는 마지막 명령의 끝으로 되돌아갑니다.

read(0, "#!/bin/bash\nsed -e 's/^/--/'\nwho"..., 36) = 36
stat("/bin/sed", {st_mode=S_IFREG|0755, st_size=73416, ...}) = 0
...
lseek(0, -7, SEEK_CUR)                  = 29
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|...

입력이 다음과 같은 파이프에서 나오는 경우:

$ cat script.sh | bash

파이프와 소켓을 검색할 수 없으므로 되감을 수 없습니다. 이 경우 Bash는 입력 읽기로 돌아갑니다.한 번에 한 문자씩너무 많이 읽는 것을 방지하기 위해. (fd_to_buffered_stream()존재하다input.c) 원칙적으로 모든 바이트에 대해 완전한 시스템 호출을 수행하는 것은 그리 효율적이지 않습니다. 실제로는 쉘이 수행하는 작업의 대부분이 완전히 새로운 프로세스를 생성하는 것과 관련된다는 사실에 비해 읽기가 큰 오버헤드가 될 것이라고 생각하지 않습니다.

비슷한 상황은 이렇습니다.

echo -e 'foo\nbar\ndoo' | bash -c 'read a; head -1'

서브쉘은 다음 줄을 보려면 read첫 번째 개행 문자만 읽도록 해야 합니다. head(이것도 적용됩니다 dash.)


즉, Bash는 스크립트 자체 및 스크립트에서 실행되는 명령과 동일한 소스 읽기를 지원하기 위해 추가 노력을 기울입니다. dash아니요. Debian에 패키지되어 zsh있으며 ksh93Bash와 함께 사용됩니다.

답변2

쉘은 표준 입력에서 스크립트를 읽고 있습니다. 스크립트 내에서 표준 입력을 읽으려는 명령을 실행합니다. 어떤 입력이 어디로 갈까요?당신은 확실히 말할 수 없습니다.

셸이 작동하는 방식은 소스 코드 블록을 읽고 구문 분석하고 찾은 경우 명령을 실행한 다음 블록의 나머지 부분과 파일의 나머지 부분을 진행하는 것입니다. 블록에 완전한 명령이 포함되어 있지 않으면 (끝에 종료 문자가 있습니다. 모든 쉘은 줄 끝까지 읽는 것 같습니다.) 쉘은 다른 블록을 읽는 식으로 진행됩니다.

스크립트의 명령이 쉘이 스크립트를 읽는 것과 동일한 파일 설명자에서 읽으려고 시도하는 경우 명령은 읽은 마지막 블록 이후의 모든 항목을 찾습니다. 이 위치는 예측할 수 없습니다. 셸에서 선택한 블록 크기에 따라 다르며 셸 및 해당 버전뿐만 아니라 시스템 구성, 사용 가능한 메모리 등에 따라 달라집니다.

Bash는 실행하기 전에 스크립트에서 명령 소스 코드의 끝을 살펴봅니다. 이것은 다른 쉘이 이 작업을 수행하지 않을 뿐만 아니라 쉘이 일반 파일에서 읽는 경우에만 작동하기 때문에 믿을 수 있는 것이 아닙니다. 쉘이 파이프(예: ssh remote-host.example.com <local-script-file.sh)에서 데이터를 읽는 경우 이미 읽은 데이터를 읽으며 취소할 수 없습니다.

스크립트의 명령에 입력을 전달하려면 일반적으로 다음을 사용하여 이를 명시적으로 수행해야 합니다.여기 문서. (여기에 있는 문서는 일반적으로 여러 줄 입력에 가장 편리하지만 어떤 접근 방식이든 가능합니다.) 작성하는 코드는 몇 개의 셸에서만 실행되며 스크립트가 일반 파일에서 셸에 입력으로 전달되는 경우에만 실행됩니다. 예상 두 번째 것은 whoami입력으로 전달됩니다 sudo …. 다시 생각해보세요. 대부분의 경우 스크립트는 쉘의 표준 입력으로 전달되지 않습니다.

#!/bin/bash
whoami
sudo su -l root <<'EOF'
whoami
EOF

이번 10년 동안 sudo -i root달리기는 sudo su과거의 기술입니다.

관련 정보