bash 파일을 표준으로 리디렉션하는 것이 Linux의 쉘 (`sh`)과 어떻게 다른가요?


9

나는 실행하는 동안 사용자를 전환하는 스크립트를 작성하고, 표준에 파일 리디렉션을 사용하여 실행. 그래서이 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합니까?

노트:

  • 데비안 제시를 실행하는 두 개의 다른 상자에서 발생

1
관련되지 않음 대답 만 sudo su: unix.stackexchange.com/questions/218169/...
Kusalananda

1
/ bin / sh = 데비안에서 대시입니까? RHEL에서 / bin / sh = bash를 재현 할 수 없습니다.
Jeff Schaller

1
/bin/sh데비안은 Dash입니다. 지금까지 그게 뭔지도 몰랐습니다. 나는 지금까지 배쉬를 고수했다.
popedotninja

1
bash는 동작 중 하나 문서화 된 의미가 작동하지 않을 수 있습니다.
Charles Duffy

누군가 내가 오늘 실행중인 스크립트가 bash가 사용되는 방식의 의미를 위반했다고 나에게 지적했다. 이 질문에 대한 몇 가지 훌륭한 답변이 있었지만 스크립트 중간에 사용자를 전환해서는 안된다는 것을 지적 할 가치가 있습니다. 원래 user-switch.shJenkins 작업을 시도했지만 스크립트가 실행되었습니다. Jenkins 외부에서 동일한 스크립트를 실행하면 다른 결과가 나오고 Jenkins에서 본 동작을 얻기 위해 파일 리디렉션을 사용했습니다.
popedotninja

답변:


12

가없는 유사한 스크립트 sudo이지만 비슷한 결과 :

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

$ bash < script.sh
--whoami

$ dash < script.sh
itvirta

bash, 스크립트의 나머지 부분에 입력으로 이동 sed하여, dash쉘 해석 그것.

strace이것들에서 실행 : dash스크립트 블록 (여기서 8 kB, 전체 스크립트를 보유하기에 충분한 것 이상)을 읽은 다음 생성합니다 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.)

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

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하지 않습니다. 는 zsh, 그리고 ksh93데비안에서 패키지는이에 배쉬과 함께 할 것입니다.


1
솔직히, 나는 그것이 작동하는 것에 약간 놀랐습니다.
ilkkachu

큰 답변 주셔서 감사합니다! strace나중에 사용하여 두 명령을 모두 실행 하고 출력을 비교합니다. 나는 그 접근법을 사용하지는 않았습니다.
popedotninja

2
네, 질문을 읽어 내 반응은 것이 놀랍지도 않습니다 bash는 함께 일을 - 나는 그것을 확실히 작동하지 않을 것을 무언가로 떨어져 제기했다. 나는 절반 밖에 못했습니다 :)이었다고 생각한다
홉스을

12

쉘이 표준 입력에서 스크립트를 읽고 있습니다. 스크립트 내에서 표준 입력을 읽고 자하는 명령을 실행합니다. 어느 입력이 어디로 갈까요? 확실하게 말할 수 없습니다 .

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

스크립트의 명령이 쉘이 스크립트를 읽는 것과 동일한 파일 디스크립터에서 읽으려고 시도하면 명령은 마지막으로 읽은 청크 뒤에 오는 것을 찾습니다. 이 위치는 예측할 수 없습니다 : 쉘이 선택한 청크 크기에 따라 달라지며 쉘과 버전뿐만 아니라 시스템 구성, 사용 가능한 메모리 등에 따라 달라질 수 있습니다.

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

스크립트에서 명령에 입력을 전달하려면 일반적으로 here document를 사용하여 명시 적으로 수행해야합니다 . (여기서 문서는 일반적으로 여러 줄 입력에 가장 편리하지만 모든 방법이 가능합니다.) 작성한 코드는 스크립트가 일반 파일에서 셸에 입력으로 전달 된 경우에만 몇 개의 셸에서만 작동합니다. 두 번째 whoami가 입력으로 전달 될 것으로 예상 한 경우 sudo …, 대부분의 경우 스크립트가 쉘의 표준 입력으로 전달되지 않는다는 점을 염두에두고 다시 생각하십시오.

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

이 10 년 동안 사용할 수 있습니다 sudo -i root. 달리기 sudo su는 과거의 해킹입니다.

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