stdin은 터미널이 아니므로 의사 터미널이 할당되지 않습니다.


345

원격 서버에 일부 디렉토리를 만든 다음 scp를 사용하여 로컬 컴퓨터에서 원격으로 파일을 복사하는 쉘 스크립트를 작성하려고합니다. 여기까지 내가 가진 것입니다 :

ssh -t user@server<<EOT
DEP_ROOT='/home/matthewr/releases'
datestamp=$(date +%Y%m%d%H%M%S)
REL_DIR=$DEP_ROOT"/"$datestamp
if [ ! -d "$DEP_ROOT" ]; then
    echo "creating the root directory"
    mkdir $DEP_ROOT
fi
mkdir $REL_DIR
exit
EOT

scp ./dir1 user@server:$REL_DIR
scp ./dir2 user@server:$REL_DIR

실행할 때마다 다음 메시지가 나타납니다.

Pseudo-terminal will not be allocated because stdin is not a terminal.

그리고 스크립트는 영원히 중단됩니다.

내 공개 키는 서버에서 신뢰할 수 있으며 스크립트 외부의 모든 명령을 잘 실행할 수 있습니다. 어떤 아이디어?


4
다음과 같이 사용할 터미널을 간단히 지정할 수 있습니다.ssh user@server /bin/bash <<EOT…
Buzut

3
@Buzut : 아마 shell을 의미 할 수도 있지만, /bin/bash명시 적으로 지정하는 것이 문제를 피하는 한 가지 방법입니다.
mklement0

1
@ mklement0 실제로, 그것은 내가 의미 한 바입니다. ;)
Buzut

답변:


512

시도 ssh -t -t(또는 ssh -tt표준 입력 단자가 아닌 경우에도 힘 의사 TTY 할당을 짧게).

참조 : bash 스크립트로 실행 된 SSH 세션 종료

ssh 맨 페이지에서 :

-T      Disable pseudo-tty allocation.

-t      Force pseudo-tty allocation.  This can be used to execute arbitrary 
        screen-based programs on a remote machine, which can be very useful,
        e.g. when implementing menu services.  Multiple -t options force tty
        allocation, even if ssh has no local tty.

21
여기에서 실행되는 스크립트에서 비슷한 문제가 있습니다. -t -t를 추가했지만 이제 새로운 오류가 발생합니다. "tcgetattr : 장치에 부적절한 ioctl"
MasterZ

5
ssh -t -t그렇지 ssh -tt않습니까? 내가 모르는 차이점이 있습니까?
Jack

3
@MasterZ 여기도 마찬가지입니다. 이에 대한 답변을 얻는 것이 좋을 것입니다Inappropriate IOCtl for device
krb686

11
@Jack -tt-t -t동일합니다. args를 개별적으로 지정하거나 함께 매끄럽게 지정하면 개인 스타일 / 선호의 문제가되며,이를 고려할 때 어느 쪽이든 수행 할 수있는 유효한 인수가 있습니다. 그러나 실제로, 그것은 단지 개인적인 취향입니다.
JDS

5
"ssh에 로컬 tty가없는 경우에도"라고 표시되면 무엇을 의미합니까?
CMCDragonkai

192

또한 옵션 -T에서 수동

의사 -tty 할당 비활성화


11
그 정답을, 그리고 너무 오래 zanco로 대답처럼, 당신은 단지 그것을 전부 건너 뛸 수있을 때, 107 점을 얻는다 "오 -t -t와 어쨌든 TTY를 할당"고 그 터무니없는 -이 대답은 더 많은 포인트를 필요로
nhed

2
방금 공언했다. 그것은 -t -t보다 나에게 더 나은 근무 않았다
빈센트 Hiribarren에게

15
'-t -t'작품은 그러나 '-T'내가 얻을로 'sudo는 죄송합니다, 당신은 sudo를 실행하는 청각 장애 있어야합니다'
이반 Balashov

3
@nhed :이 -tt옵션은 실제로 TTY를 원하고 OP의 오류 메시지가 표시되는 사용자에게 가장 적합합니다. 이 -T답변은 TTY가 필요하지 않은 경우에 더 좋습니다.
ErichBSchulz

3
@nhed-물론-그러나 나는 나와 같은 다른 사람들이 오류 메시지의 오류를 인터넷 검색에서 얻었습니다. 다른 Google 직원이 2 개의 경쟁 답변 중에서 어떻게 선택해야하는지 분명히하는 것은 무리가 없습니다. 아니면 아닐 수도 있습니다. 몰라요
ErichBSchulz

91

zanco의 대답 , 당신은에 원격 명령을 제공하지 않는 ssh쉘 명령 줄을 구문 분석하는 방법을 제공. 이 문제를 해결하려면 ssh명령 호출 구문을 변경하여 원격 명령이 구문 상 올바른 다중 행 문자열로 구성되도록하십시오.

사용할 수있는 다양한 구문이 있습니다. 예를 들어, 명령을 bashand sh및 다른 쉘로 파이프 할 수 있으므로 가장 간단한 해결책은 ssh쉘 호출을 heredocs와 결합 하는 것입니다.

ssh user@server /bin/bash <<'EOT'
echo "These commands will be run on: $( uname -a )"
echo "They are executed by: $( whoami )"
EOT

위의 내용 없이 실행 /bin/bash하면 경고가 표시 Pseudo-terminal will not be allocated because stdin is not a terminal됩니다. 또한 EOT작은 따옴표 bash묶여서 heredoc을 nowdoc 로 인식하여 로컬 변수 보간을 끄고 명령 텍스트가 그대로 전달되도록하십시오 ssh.

파이프 팬이라면 다음과 같이 위의 내용을 다시 쓸 수 있습니다.

cat <<'EOT' | ssh user@server /bin/bash
echo "These commands will be run on: $( uname -a )"
echo "They are executed by: $( whoami )"
EOT

위에도 동일한 경고가 /bin/bash적용됩니다.

또 다른 유효한 방법은 bash다음과 같이 여러 계층의 변수 보간을 사용하여 여러 줄 원격 명령을 단일 문자열로 전달하는 것입니다.

ssh user@server "$( cat <<'EOT'
echo "These commands will be run on: $( uname -a )"
echo "They are executed by: $( whoami )"
EOT
)"

위의 솔루션은 다음과 같은 방식으로이 문제를 해결합니다.

  1. ssh user@serverbash에 의해 구문 분석되고으로 해석되어 ssh명령, 인수 다음에 user@server받는 전달되는 ssh명령

  2. "완료 될 때 ssh명령에 전달 될 인수를 구성하는 보간 된 문자열을 시작합니다 .이 경우이 ssh명령은 다음과 같이 실행할 원격 명령 으로 해석 됩니다.user@server

  3. $( 주변 보간 된 문자열에 의해 출력이 캡처되어 실행될 명령을 시작합니다.

  4. cat뒤에 오는 모든 파일의 내용을 출력하는 명령입니다. 의 출력은 cat캡처 보간 문자열로 다시 전달됩니다.

  5. <<bash heredoc을 시작합니다

  6. 'EOT'heredoc의 이름이 EOT임을 지정합니다. 'EOT를 둘러싼 작은 따옴표 는 heredoc이 nowdoc 로 구문 분석되어야 함을 지정합니다. nowdoc 는 내용이 bash에 의해 보간되지 않고 리터럴 형식으로 전달 되는 heredoc 의 특수 형식입니다.

  7. 사이에 <<'EOT'있고 <newline>EOT<newline>nowdoc 출력에 추가 될 모든 컨텐츠

  8. EOTnowdoc을 종료하여 nowdoc 임시 파일을 작성하여 호출 cat명령으로 다시 전달 합니다. catnowdoc를 출력하고 출력을 캡처 된 보간 된 문자열로 다시 전달

  9. ) 실행할 명령을 종료합니다

  10. "보간 문자열 캡처를 마칩니다. 보간 된 문자열의 내용은 ssh단일 명령 행 인수로 다시 전달되어 다음 과 같이 ssh실행할 원격 명령으로 해석됩니다.user@server

과 같은 외부 도구를 사용 cat하지 말고 하나 대신 두 개의 명령문을 사용하지 않아도 read되는 경우 heredoc과 함께 내장을 사용하여 SSH 명령을 생성하십시오.

IFS='' read -r -d '' SSH_COMMAND <<'EOT'
echo "These commands will be run on: $( uname -a )"
echo "They are executed by: $( whoami )"
EOT

ssh user@server "${SSH_COMMAND}"

1 년 반 후에 +1! :) 정말 명확한 설명. 잘 했어요
yaroslavTir

1
나를 위해 (그리고 나는 결코 "DevOps"사람이 아닙니다) 이것이 효과가 있습니다 (Jenkins를 사용하고 있습니다). 또한 "-t -t"및 "-T"제안을 시도했지만 공개 스트림 문제와 비 실행 문제가 발생했습니다.
Ryan Crews

2 년 후, 정말 도움이 됨
jack-nie

+1 멋진 설명. 그러나 마지막 코드 스 니펫 (IFS = ''*)을 원격 명령을 실행하는 함수로 만들면 어떻게 $ 1을 IFS = ''*에 전달합니까? 그것은 $ 1의 내용을 전혀 인식하지 못하는 것 같습니다.
Cristian Matthias Ambæk

1
@ mklement0 물론 필요에 따라 문자열을 이스케이프 할 수 있지만 다른 쉘 스크립트에서 복사하여 붙여 넣거나 스크립트를 수정 해야하는 번거 로움을 원하는 사람은 누구입니까? 또한 cat솔루션 의 우아함은 단일 (복합) 문에서 수행되며 임시 변수가 쉘 환경을 오염시키지 않는다는 것입니다.
Dejay Clayton

63

이 오류는 동일한 오류 메시지로 발생하는 관련 문제를 해결했기 때문에 추가합니다.

문제 : Windows에서 cygwin을 설치 했는데이 오류가 발생했습니다.Pseudo-terminal will not be allocated because stdin is not a terminal

해결 : openssh 클라이언트 프로그램과 유틸리티를 설치 하지 않은 것으로 나타났습니다 . 그 cygwin 때문에 cygwin 버전이 아닌 ssh의 Windows 구현을 사용하고있었습니다. 해결책은 openssh cygwin 패키지를 설치하는 것이 었습니다.


10
나를 위해 그것은 PATH에서 또 다른 ssh 구현이라고 밝혀졌습니다.$ which ssh/cygdrive/c/Program Files (x86)/Git/bin/ssh
FelixJongleur42

및 설치 openssh:이 것은 Windows 용 이동하는 방법이있을 수 있습니다 superuser.com/a/301026/260710
안드레아스 디트리히

이 솔루션은 저에게 효과적입니다. openssh를 설치하면 문제가 사라집니다.
jdhao

34

경고 메시지 Pseudo-terminal will not be allocated because stdin is not a terminal.sshstdin이 here 문서에서 리디렉션되는 동안 명령이 지정되지 않았기 때문입니다 . 인수로 지정된 명령이 없기 때문에 ssh먼저 원격 호스트에 pty를 할당해야하는 대화식 로그인 세션이 필요하지만 로컬 stdin이 tty / pty가 아님을 인식해야합니다. sshhere 문서에서 stdin을 리디렉션 하려면 일반적으로 명령 (예 /bin/sh:)을 인수로 지정해야합니다 ssh. 이러한 경우 기본적으로 원격 호스트에 pty가 할당되지 않습니다.

통해 실행 할 명령이 없기 때문에 ssh그 (같은 TTY / PTY의 존재를 필요로 vim하거나 top) -t로 전환 ssh불필요하다. 그냥 사용 ssh -T user@server <<EOT ...또는 ssh user@server /bin/bash <<EOT ...과 경고가 사라집니다.

경우 <<EOF이스케이프되지 않았거나 작은 따옴표 (즉, <<\EOT또는 <<'EOT'그것을 실행하기 전에 여기에 문서 내부) 변수는 로컬 쉘에 의해 확장됩니다 ssh .... 결과는 here 문서 내부의 변수가 원격 쉘에서만 정의되므로 비어있게됩니다.

따라서 $REL_DIR로컬 셸에서 액세스 할 수 있고 원격 셸에서 $REL_DIR정의되어야하는 경우 ssh명령 전에 here 문서 외부에서 정의해야합니다 ( 아래 버전 1 ). 경우 나, <<\EOT또는 <<'EOT'사용하는 경우, 출력 ssh명령이 할당 될 수 REL_DIR의 유일한 출력 경우 ssh표준 출력 명령에 의해 genererated되는 echo "$REL_DIR"(가) / 단일 여기 인용 된 문서 (탈출 내부 버전 2 이하에게).

세 번째 옵션은 here 문서를 변수에 저장 한 다음이 변수를 명령 인수로 전달합니다 ssh -t user@server "$heredoc"( 아래 버전 3 ).

그리고 마지막으로, 원격 호스트의 디렉토리가 성공적으로 작성되었는지 확인하는 것은 좋지 않습니다 ( ssh를 사용하여 원격 호스트에 파일이 있는지 확인 참조 ).

# version 1

unset DEP_ROOT REL_DIR
DEP_ROOT='/tmp'
datestamp=$(date +%Y%m%d%H%M%S)
REL_DIR="${DEP_ROOT}/${datestamp}"

ssh localhost /bin/bash <<EOF
if [ ! -d "$DEP_ROOT" ] && [ ! -e "$DEP_ROOT" ]; then
   echo "creating the root directory" 1>&2
   mkdir "$DEP_ROOT"
fi
mkdir "$REL_DIR"
#echo "$REL_DIR"
exit
EOF

scp -r ./dir1 user@server:"$REL_DIR"
scp -r ./dir2 user@server:"$REL_DIR"


# version 2

REL_DIR="$(
ssh localhost /bin/bash <<\EOF
DEP_ROOT='/tmp'
datestamp=$(date +%Y%m%d%H%M%S)
REL_DIR="${DEP_ROOT}/${datestamp}"
if [ ! -d "$DEP_ROOT" ] && [ ! -e "$DEP_ROOT" ]; then
   echo "creating the root directory" 1>&2
   mkdir "$DEP_ROOT"
fi
mkdir "$REL_DIR"
echo "$REL_DIR"
exit
EOF
)"

scp -r ./dir1 user@server:"$REL_DIR"
scp -r ./dir2 user@server:"$REL_DIR"


# version 3

heredoc="$(cat <<'EOF'
# -onlcr: prevent the terminal from converting bare line feeds to carriage return/line feed pairs
stty -echo -onlcr
DEP_ROOT='/tmp'
datestamp="$(date +%Y%m%d%H%M%S)"
REL_DIR="${DEP_ROOT}/${datestamp}"
if [ ! -d "$DEP_ROOT" ] && [ ! -e "$DEP_ROOT" ]; then
   echo "creating the root directory" 1>&2
   mkdir "$DEP_ROOT"
fi
mkdir "$REL_DIR"
echo "$REL_DIR"
stty echo onlcr
exit
EOF
)"

REL_DIR="$(ssh -t localhost "$heredoc")"

scp -r ./dir1 user@server:"$REL_DIR"
scp -r ./dir2 user@server:"$REL_DIR"

5
이 오류의 의미와 heredoc을 사용할 때 오류가 나타나는 이유에 대한 매우 자세한 설명에 감사드립니다.이 문제를 해결하기보다는 실제로 이해하는 데 도움이되었습니다.
Dan

29

모든 관련 정보는 기존 답변에 있지만 실용적인 요약을 시도해 보겠습니다 .

tl; dr :

  • 명령 행 인수를 사용하여 실행할 명령을 전달하십시오 .
    ssh jdoe@server '...'

    • '...' 문자열은 여러 줄에 걸쳐있을 수 있으므로 여기 문서를 사용하지 않아도 코드를 읽을 수 있습니다.
      ssh jdoe@server ' ... '
  • here-document 를 사용할 때와 같이 stdin을 통해 명령을 전달하지 마십시오 .
    ssh jdoe@server <<'EOF' # Do NOT do this ... EOF

명령을 인수로 전달하면 그대로 작동합니다.

  • 의사 터미널의 문제는 발생하지 않습니다.
  • exit명령이 처리 된 후 세션이 자동으로 종료되므로 명령 끝에 명령문이 필요하지 않습니다 .

간단히 말해 : stdin을 통해 명령을 전달 하는 것은 ssh의 설계와 상충되는 메커니즘 이며 문제를 해결해야합니다.
더 알고 싶다면 계속 읽으십시오.


선택적 배경 정보 :

ssh대상 서버에서 실행할 명령을 받아들이는 메커니즘은 명령 행 인수입니다 . 최종 피연산자 (옵션이 아닌 인수)는 하나 이상의 쉘 명령이 포함 된 문자열을 허용합니다.

  • 기본적으로 이러한 명령 은 (의사) 터미널을 사용하지 않고 비 대화식 셸 에서 무인으로 실행되며 (옵션 -T이 암시 됨) 마지막 명령이 처리를 마치면 세션이 자동으로 종료 됩니다.

  • 대화식 프롬프트에 응답하는 것과 같이 명령에 사용자 상호 작용이 필요한 경우 , 옵션을 사용하여 원격 세션과 상호 작용할 수있는 의사 터미널 인 pty (pseudo-tty) 작성을 명시 적으로 요청할 수 있습니다 -t. 예 :

    • ssh -t jdoe@server 'read -p "Enter something: "; echo "Entered: [$REPLY]"'

    • 대화식 read프롬프트는 pty에서만 올바르게 작동하므로 -t옵션이 필요합니다.

    • pty를 사용하면 주목할만한 부작용이 있습니다. stdout과 stderr이 결합 되고 stdout을 통해보고됩니다 . 다시 말해, 정규 출력과 오류 출력의 구별을 잃습니다. 예 :

      • ssh jdoe@server 'echo out; echo err >&2' # OK - stdout and stderr separate

      • ssh -t jdoe@server 'echo out; echo err >&2' # !! stdout + stderr -> stdout

이 인수가 없으면ssh 문제가 시작되는 stdin을 통해 명령을 보낼 때를 포함 하여 대화식 쉘을 만듭니다 .

  • 를 들어 대화 형 쉘, ssh일반적으로 기본적으로 PTY (의사 터미널) 할당 을 제외하고 는 표준 입력이 터미널 A (실제)에 연결되지 않은 경우.

    • stdin을 통해 명령을 보내면 ssh의 stdin이 더 이상 터미널에 연결되어 있지 않으므로 pty가 생성 되지 않으며 이에 따라 ssh 경고 합니다 .
      Pseudo-terminal will not be allocated because stdin is not a terminal.

    • 심지어 -t그 표현 목적입니다 옵션, 요청 PTY의 창조이며, 하지 충분 이 경우 : 동일한 경고를 얻을 것이다.

      • 다소 호기심, 당신은해야한다 두 배-t 옵션 하십시오 PTY의 힘 창출 ssh -t -t ...또는 ssh -tt ...당신이 있음을 나타냅니다 정말, 정말 그것을 의미를 .

      • 아마도이 매우 신중한 단계를 요구하는 근거는 상황이 예상대로 작동하지 않을 수 있다는 입니다. 예를 들어, macOS 10.12에서 stdin을 통해 명령을 제공하고을 사용하여 위 명령과 똑같은 기능이 제대로 작동 -tt하지 않습니다 . read프롬프트에 응답 한 후 세션이 중단 됩니다.
        ssh -tt jdoe@server <<<'read -p "Enter something: "; echo "Entered: [$REPLY]"'


인수로 전달하려는 명령이 시스템에 대해 명령 행을 너무 길게 만드는 경우 (길이가 가까울 경우 getconf ARG_MAX- 이 기사 참조 ), 먼저 스크립트 형식으로 원격 시스템에 코드를 복사하는 것을 고려 하십시오 ( )를 사용하여 scp해당 스크립트를 실행하는 명령을 보냅니다.

핀치에서을 사용 -T하고 stdin을 통해 exit명령 에 후행 명령을 제공하십시오. 그러나 대화식 기능이 필요한 경우 -tt대신 -T사용할 수 없습니다.


1
이것은 -tt 방식으로 터미널이 ssh를 통해 전송 된 모든 명령을 표시하도록 완벽하게 작동했습니다.
Mark E

1
"pty를 강제로 작성하려면 -t 옵션을 두 번 사용하십시오. ssh -t -t ... 또는 ssh -tt ...는 당신이 정말로 의미한다는 것을 보여줍니다." -하하하-재미 있지만 진심으로 고마워요-내가 단 하나의 t를 썼을 때 그것이 깨지지 않는 두 배의 t 주위에 머리를 넣을 수 없습니다
DavidC

22

중단이 어디에서 왔는지 모르겠지만 대화 형 ssh로 명령을 리디렉션 (또는 파이핑)하는 것이 일반적으로 문제의 레시피입니다. command-to-run-as-a-last-argument 스타일을 사용하고 ssh 명령 행에서 스크립트를 전달하는 것이 더 강력합니다.

ssh user@server 'DEP_ROOT="/home/matthewr/releases"
datestamp=$(date +%Y%m%d%H%M%S)
REL_DIR=$DEP_ROOT"/"$datestamp
if [ ! -d "$DEP_ROOT" ]; then
    echo "creating the root directory"
    mkdir $DEP_ROOT
fi
mkdir $REL_DIR'

(하나의 거대 '구분 된 여러 줄 명령 줄 인수에서 모두).

의사 터미널 메시지는 -tssh가 원격 시스템에서 실행되는 환경을 거기서 실행되는 프로그램의 실제 터미널처럼 보이도록 요청하기 때문입니다. ssh 클라이언트는 자체 표준 입력이 터미널이 아니기 때문에 원격 시스템에서 로컬 터미널의 실제 터미널로 특수 터미널 API를 전달할 수 없습니다.

-t어쨌든 무엇 을 달성하려고 했습니까?


1
-t 옵션은 Psuedo 터미널 문제를 해결하려는 시도였습니다 (작동하지 않았습니다). 나는 당신의 해결책을 시도했는데, 그것은 psuedo 터미널을 제거했지만 이제는 그냥 멈췄습니다 ...
Matthew

4
@Henning : 대화식 ssh로 경로 재 지정 또는 파이핑의 단점에 대해 자세히 설명하거나 링크를 제공 할 수 있습니까?
Isaac Kleinman

조금 늦었지만 여기에 의사 터미널이 필요하지 않으므로 -T대신 옵션을 사용하십시오.
Florian Castellane

6

이 답변을 많이 읽은 후 결과 솔루션을 공유한다고 생각했습니다. 내가 추가 한 것은 /bin/bashheredoc 앞에 있으며 더 이상 오류를주지 않습니다.

이것을 사용하십시오 :

ssh user@machine /bin/bash <<'ENDSSH'
   hostname
ENDSSH

이 대신 (오류가 발생 함) :

ssh user@machine <<'ENDSSH'
   hostname
ENDSSH

또는 이것을 사용하십시오 :

ssh user@machine /bin/bash < run-command.sh

이 대신 (오류가 발생 함) :

ssh user@machine < run-command.sh

엑스트라 :

원격 대화식 프롬프트가 계속 필요한 경우 (예 : 원격으로 실행중인 스크립트에서 비밀번호 나 기타 정보를 묻는 메시지가 표시되는 경우) 이전 솔루션에서 프롬프트를 입력 할 수 없기 때문입니다.

ssh -t user@machine "$(<run-command.sh)"

또한 전체 세션을 파일에 기록하려면 다음을 수행하십시오 logfile.log.

ssh -t user@machine "$(<run-command.sh)" | tee -a logfile.log

0

Windows에서 emacs 24.5.1을 사용하여 / ssh : user @ host를 통해 일부 회사 서버에 연결하는 것과 동일한 오류가 발생했습니다. 내 문제를 해결 한 것은 "tramp-default-method"변수를 "plink"로 설정하고 서버에 연결할 때마다 ssh 프로토콜을 생략합니다. 이것이 작동하려면 PuTTY의 plink.exe가 설치되어 있어야합니다.

해결책

  1. Mx customize-variable (Enter를 누르십시오)
  2. tramp-default-method (그리고 Enter를 다시 누르십시오)
  3. 텍스트 필드에 plink를 넣고 버퍼를 적용하고 저장하십시오.
  4. 원격 서버에 액세스하려고 할 때마다 Cxf / user @ host :를 사용하고 암호를 입력하십시오. 이제 Windows의 Emacs에서 원격 서버로 올바르게 연결되었습니다.

-3

ssh -t foobar @ localhost yourscript.pl


2
OP도 사용 -t하지만 특정 시나리오로는 충분하지 않아 질문이 시작되었습니다.
mklement0
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.