script1.sh
다음 소스가 있다고 가정해 보겠습니다 script2.sh
.
스크립트 1:
#!/bin/bash
# script 1
echo "Doing some stuff..."
bash script2.sh
echo "Done"
스크립트 2:
#!/bin/bash
# script 2
printf "\r [ \033[0;33m?\033[0m ] What is your name? "
read -e name
echo "$name"
그리고 통화 bash script1.sh
도 우아하게 작동합니다.
나는 다음과 같은 것을하려고 노력하고 있습니다.여기그리고 실행 하지만 스크립트를 wget -O - https://raw.githubusercontent.com/caarlos0/dotfiles/install/script/install | bash
실행하면 에서 실패합니다 .script/bootstrap
read -e
상호 작용을 강제하기 위해 이 플래그를 사용해 보았지만 -i
성공하지 못했습니다.
어떤 아이디어가 있나요?
답변1
아이디어: bash
표준 입력에서 파이프를 사용하여 실행하고 있습니다. 그런 다음 표준 입력에서 읽도록 요청합니다. 그건 작동하지 않습니다.
대신 명명된 파이프를 사용하시겠습니까?
ETA: 아니면 아닐지도…
mkfifo ~/tmp-pipe
wget -O - https://raw.githubusercontent.com/caarlos0/dotfiles/install/script/install > ~/tmp-pipe &
bash ~/tmp-pipe
rm ~/tmp-pipe
이 시점에서 일부 기존 파일이 손상되지 않았는지 확인하려면 임시 파일을 사용하는 것이 좋습니다. 처리해야 할 혼란이 줄어듭니다.
P=$( mktemp )
wget -O - https://raw.githubusercontent.com/caarlos0/dotfiles/install/script/install > $P &
bash $P
rm $P
예. 나는 모른다.
답변2
bash 프로세스의 표준 입력은 wget에서 다운로드한 스크립트입니다. 따라서 호출은 read
스크립트에서 한 줄을 읽거나 bash가 이미 스크립트를 완료한 경우 아무것도 읽지 않습니다.
wget -O - … | bash
어쩌면 귀여운 농담일 수도 있지만 좋은 생각은 아닙니다. 다운로드가 중단되면 bash는 다운로드할 수 있는 스크립트 부분을 실행하므로 wget의 오류는 감지하기 어려울 수 있습니다. 먼저 파일을 다운로드한 후 실행하는 것이 좋습니다.
wget https://…/install
bash install
rm install
임시 파일을 명시적으로 생성하지 않으려면 프로세스 대체를 사용할 수 있습니다. 이를 위해서는 대화형 셸이 프로세스 대체를 지원해야 합니다. 즉, bash, ksh93 또는 zsh여야 합니다. 이는 파이프와 동일한 단점이 있습니다. 다운로드가 중단되더라도 스크립트는 실행되지만 여전히 터미널인 표준 입력을 얻지 못합니다.
bash <(wget -O - …)
스크립트가 wget -O - … | bash
파이프 메소드를 지원하도록 하려면 를 전달하면 됩니다 exec </dev/tty
. 그러나 이는 좋지 않은 생각이며 단순히 응답을 제공하는 것만으로는 스크립트를 자동화할 수 없기 때문에 많은 사용자가 당신을 저주하게 될 것입니다. 표준 입력 - 대신 터미널에서 스크립트를 실행하는 것과 같은 것을 사용해야 expect
하거나 자동으로 사용할 수도 있습니다.
이 -i
옵션은 관련이 없습니다. bash는 스크립트를 실행하기 때문에 대화식으로 실행되지 않으며 표준 입력은 파이프에서 리디렉션되기 때문에 터미널이 아닙니다. 이 -i
옵션을 켜면 표준 입력으로 스크립트가 전달되더라도 bash는 대화식으로 사용되는 척하게 됩니다. 이는 표준 입력이 터미널이 아니라는 사실을 바꾸지 않습니다.
그건 그렇고, 이것은 스크립트가 두 부분으로 분할되는 것과는 아무런 관련이 없습니다. 스크립트 2의 코드가 기본 스크립트에 있으면 정확히 동일한 내용이 표시됩니다.
답변3
다른 사람들은 표준 입력이 스트림 스크립트로 대체되고 있다고 언급했습니다. 이는 일반적인 문제이지만 해결하기 쉽습니다.
bash 3<&0 <<\SCRIPT #ref stdin in fd 3, replace stdin w/ SCRIPT
echo Doing some stuff... #do your stuff
printf "\r [ \033[0;33m?\033[0m ] What is your name? "
read -e name <&3 #read from original stdin
echo "$name"
echo Done
SCRIPT
위 시퀀스에서는 파이프라인 스크립트 대신 heredoc을 사용하고 있습니다. 파이프가 아직 bash
위의 표준 입력을 이전 내용으로 대체 하지 않았기 때문에(보통 터미널)위의 것은 단지 하나입니다.작은스크립트가 필요한 것보다 간단합니다. 하지만 나는 그것이 어떻게 작동하는지 보여주고 싶고 이것이 가장 간단한 방법입니다.
호출 되면 bash
나중에 실행되는 모든 프로세스는 명시적으로 닫지 않는 한 모든 fd를 상속합니다 >&-
. 따라서 통화 시 간단한 리디렉션을 수행하면 해당 리디렉션이 모든 하위 항목으로 전달됩니다. 위의 fd 0을 fd 3에 복사하면 read <&3
나중에 같은 성가신 작업을 수행하지 않고도 동일한 fd를 사용할 수 있습니다 </dev/tty
. 사용자가 키보드를 조작하는 것보다 사용자의 키보드와 상호 작용할 합당한 이유가 있는 경우 후자가 아마도 좋은 생각일 것입니다. 당신에게 말할 수도 있습니다.
그럼에도 불구하고 표준 출력은 여전히 같은 위치로 이동합니다. 따라서 모든 echo/printf
출력 스트림이 어떤 출력 스트림으로든 인쇄되지만 스트림 입력 스크립트에서 bash
가져오며 stdin
이에 대한 다른 참조는 명시적이어야 합니다.
파이프라인에는 레이어가 하나 더 필요합니다.
{ wget ... | bash; } 3<&0
이것은 거의 같은 방식으로 작동합니다. 이렇게 하려면 <&3
스트림을 명시적으로 참조할 필요가 없습니다.read
(또는 다른 명령):
{ { echo "script$$(){"
wget ...
printf "\n} <&3; script$$\n"
} | bash
} 3<&0
이렇게 하면 실제로 전체 스크립트를 파이프하고 전체 내용을 읽은 후 저장된 표준 입력을 함수의 표준 입력으로 명시적으로 리디렉션하는 함수를 정의할 수 있습니다.그 다음에당신은 그것을 부릅니다. 따라서 여기에 포함된 모든 명령은 표준 입력으로 처리되며 <&3
이를 참조하기 위해 내용을 변경할 필요가 없지만 표준 입력을 통해 전체 내용을 계속 읽을 수 있습니다.