Pseudo-terminal จะไม่ถูกจัดสรรเนื่องจาก 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: คุณอาจหมายถึงเชลล์แต่ใช่การระบุ/bin/bashอย่างชัดเจนเป็นวิธีหนึ่งในการหลีกเลี่ยงปัญหา
mklement0

1
@ mklement0 แน่นอนนั่นคือสิ่งที่ฉันหมายถึง ขอบคุณสำหรับการแก้ไขที่มิ)
Buzut

คำตอบ:


512

ลองssh -t -t(หรือssh -ttสั้น ๆ ) เพื่อบังคับใช้การจัดสรรหลอกแม้ว่า stdin ไม่ใช่เทอร์มินัล

ดูเพิ่มเติม: การยกเลิกเซสชัน SSH ที่ดำเนินการโดยสคริปต์ทุบตี

จาก ssh manpage:

-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? มีความแตกต่างที่ฉันไม่ทราบหรือไม่?
แจ็ค

3
@MasterZ สิ่งเดียวกันที่นี่ มันคงจะดีถ้าได้รับคำตอบสำหรับสิ่งนี้Inappropriate IOCtl for device
krb686

11
@ แจ็ค-ttและ-t -tเทียบเท่า; การระบุ args แยกต่างหากหรือ smooshed ร่วมกันกลายเป็นเรื่องของสไตล์ / ความชอบส่วนตัวและเมื่อมันลงมามีข้อโต้แย้งที่ถูกต้องสำหรับการทำมันด้วยวิธีใด แต่จริงๆแล้วมันเป็นเพียงการตั้งค่าส่วนตัว
JDS

5
หมายความว่าอย่างไรเมื่อมีข้อความว่า "แม้ว่า ssh จะไม่มี tty ในเครื่อง"?
CMCDragonkai

192

นอกจากนี้ยังมีตัวเลือก-Tจากคู่มือ

ปิดใช้งานการจัดสรรแบบหลอก


11
คำตอบนี้ต้องการคะแนนมากขึ้น - เป็นคำตอบที่ถูกต้องและไม่นานเหมือนคำตอบของ zanco มันไร้สาระที่ "โอ้จัดสรร TTY แล้วด้วย -t -t" ได้รับ 107 คะแนนเมื่อคุณสามารถข้ามมันไปได้ทั้งหมด
nhed

2
upvoted เพียง; มันทำงานได้ดีกว่าสำหรับฉันมากกว่า -t -t
Vincent Hiribarren

15
งาน '-t -t' อย่างไรก็ตามด้วย '-T' ฉันได้รับ 'sudo: ขออภัยคุณต้องมี tty เพื่อให้ทำงาน sudo'
Ivan Balashov

3
@nhed: -ttตัวเลือกที่ดีที่สุดสำหรับพวกเราที่ต้องการ TTY และได้รับข้อความแสดงข้อผิดพลาดของ OP นี้-Tคำตอบคือดีกว่าถ้าคุณไม่จำเป็นต้อง TTY
ErichBSchulz

3
@ ไม่มี - แน่นอน - แต่ฉันแน่ใจว่าคนอื่นเช่นฉันได้รับที่นี่จาก Googling ข้อผิดพลาดในข้อความแสดงข้อผิดพลาด ดูเหมือนว่าไม่มีเหตุผลที่ชัดเจนว่าชาว Google คนอื่น ๆ ควรเลือกระหว่าง 2 คำตอบที่แข่งขันกัน หรืออาจจะไม่ ฉันไม่รู้
ErichBSchulz

91

ตามคำตอบของ zancoคุณไม่ได้ให้คำสั่งระยะไกลเพื่อsshให้เชลล์แจงคำสั่งบรรทัดคำสั่ง เมื่อต้องการแก้ไขปัญหานี้ให้เปลี่ยนไวยากรณ์ของsshการเรียกใช้คำสั่งของคุณเพื่อให้คำสั่งระยะไกลประกอบด้วยสตริงหลายบรรทัดที่ถูกต้องทางไวยากรณ์

มีไวยากรณ์ที่หลากหลายที่สามารถใช้ได้ ตัวอย่างเช่นเนื่องจากคำสั่งสามารถไพพ์ลงในbashและ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 เป็นnowdocsshปิดการแก้ไขตัวแปรท้องถิ่นเพื่อให้ข้อความคำสั่งจะถูกส่งผ่านไปตามที่เป็นไป

หากคุณเป็นแฟนของท่อคุณสามารถเขียนข้างต้นดังต่อไปนี้:

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@serverถูกแยกวิเคราะห์โดย bash และตีความว่าเป็นsshคำสั่งตามด้วยอาร์กิวเมนต์ที่user@serverจะถูกส่งผ่านไปยังsshคำสั่ง

  2. "เริ่มต้นสตริง interpolated ซึ่งเมื่อเสร็จแล้วจะประกอบด้วยอาร์กิวเมนต์ที่จะส่งผ่านไปยังsshคำสั่งซึ่งในกรณีนี้จะถูกตีความโดยsshเป็นคำสั่งระยะไกลเพื่อดำเนินการตามuser@server

  3. $( เริ่มต้นคำสั่งที่จะดำเนินการกับเอาท์พุทที่ถูกจับโดยสตริง interpolated โดยรอบ

  4. catเป็นคำสั่งเพื่อส่งออกเนื้อหาของไฟล์ใดก็ตามที่ตามมา เอาต์พุตของcatจะถูกส่งกลับไปยังสตริงการแก้ไขที่จับภาพ

  5. <<เริ่มheredocทุบตี

  6. 'EOT'ระบุว่าชื่อของ heredoc คือ EOT เครื่องหมายอัญประกาศเดี่ยว'ล้อมรอบ EOT ระบุว่า heredoc ควรได้รับการแจงเป็นnowdocซึ่งเป็นรูปแบบพิเศษของ heredoc ซึ่งเนื้อหาไม่ได้รับการสอดแทรกด้วย bash แต่ส่งผ่านในรูปแบบตัวอักษร

  7. เนื้อหาใด ๆ ที่พบระหว่าง<<'EOT'และ<newline>EOT<newline>จะถูกผนวกเข้ากับเอาต์พุต nowdoc

  8. EOTยุติ nowdoc ทำให้ไฟล์ nowdoc ชั่วคราวถูกสร้างขึ้นและส่งกลับไปยังcatคำสั่งการโทร catเอาต์พุต nowdoc และส่งผ่านเอาต์พุตกลับไปยังสตริงการแก้ไขที่จับภาพ

  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 อีกหนึ่งปีครึ่งต่อมา! :) คำอธิบายที่ชัดเจนจริงๆ ทำได้ดีมาก
yaroslavTir

1
สำหรับฉัน (และฉันไม่ได้เป็นคน "DevOps") นี่คือสิ่งที่ได้ผล (ฉันใช้เจนกินส์) ฉันยังลองใช้คำแนะนำ "-t -t" และ "-T" แต่พบปัญหาเกี่ยวกับสตรีมแบบเปิดและปัญหาที่ไม่ได้ดำเนินการ
Ryan Crews

2 ปีต่อมามีประโยชน์จริงๆ
jack-nie

+1 คำอธิบายที่ยอดเยี่ยม แต่ถ้าคุณสร้างโค้ดสุดท้าย (IFS = '' *) ฟังก์ชั่นสำหรับรันคำสั่งรีโมตคุณจะส่ง $ 1 ไปยัง IFS = '' * ได้อย่างไร? มันดูเหมือนว่า docent จะรับรู้เนื้อหาของ $ 1 เลย
Cristian Matthias Ambæk

1
@ mklement0 แน่นอนคุณสามารถหลีกเลี่ยงสตริงได้ตามต้องการ แต่ใครต้องการความยุ่งยากในการแก้ไขคำสั่งสคริปต์ที่คุณเพิ่งจะคัดลอกและวางจากเชลล์สคริปต์อื่นหรือสแต็คโอเวอร์โฟลว์สำหรับเรื่องนั้น นอกจากนี้ความสง่างามของcatการแก้ปัญหาก็คือมันสามารถทำได้ในคำสั่งเดียว (แม้ว่าสารประกอบ) และไม่ส่งผลให้ตัวแปรชั่วคราวก่อให้เกิดมลพิษสภาพแวดล้อมของเชลล์
Dejay Clayton

63

ฉันกำลังเพิ่มคำตอบนี้เพราะมันแก้ปัญหาที่เกี่ยวข้องที่ฉันมีกับข้อผิดพลาดเดียวกัน

ปัญหา : ฉันได้ติดตั้ง cygwin ใน Windows และได้รับข้อผิดพลาดนี้:Pseudo-terminal will not be allocated because stdin is not a terminal

การแก้ไข : ปรากฎว่าฉันไม่ได้ติดตั้งโปรแกรมไคลเอนต์ openssh และยูทิลิตี้ เนื่องจาก cygwin นั้นใช้การติดตั้ง ssh ไม่ใช่รุ่น cygwin ทางออกคือการติดตั้งแพคเกจ openssh cygwin


10
สำหรับฉันมันเปิดออกมันก็เป็นอีก implementiation SSH ในเส้นทาง:$ which ssh/cygdrive/c/Program Files (x86)/Git/bin/ssh
FelixJongleur42

และเพื่อติดตั้งopensshสิ่งนี้อาจเป็นหนทางไปสู่ ​​Windows: superuser.com/a/301026/260710
Andreas Dietrich

วิธีนี้ใช้ได้ผลสำหรับฉัน หลังจากติดตั้ง openssh ปัญหาจะหายไป
jdhao

34

ข้อความเตือนPseudo-terminal will not be allocated because stdin is not a terminal.เกิดขึ้นเนื่องจากข้อเท็จจริงที่ว่าไม่มีการระบุคำสั่งsshในขณะที่ stdin ถูกเปลี่ยนเส้นทางจากเอกสารที่นี่ เนื่องจากการขาดคำสั่งที่ระบุเป็นอาร์กิวเมนต์sshแรกคาดว่าเซสชันการเข้าสู่ระบบแบบโต้ตอบ (ซึ่งจะต้องมีการจัดสรร pty บนโฮสต์ระยะไกล) แต่แล้วก็ต้องตระหนักว่า stdin ท้องถิ่นของมันคือ tty / pty การเปลี่ยนเส้นทางsshของ 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 ...ตัวแปรภายในเอกสารที่นี่จะได้รับการขยายตัวเปลือกท้องถิ่นก่อนที่จะมีการดำเนินการ ผลที่ได้คือตัวแปรภายในเอกสารที่นี่จะยังคงว่างเปล่าเพราะถูกกำหนดไว้ในเปลือกระยะไกลเท่านั้น

ดังนั้นหาก$REL_DIRทั้งสองสามารถเข้าถึงได้โดยเชลล์ท้องถิ่นและกำหนดไว้ในเปลือกระยะไกล$REL_DIRจะต้องมีการกำหนดไว้นอกเอกสารที่นี่ก่อนsshคำสั่ง ( รุ่น 1ด้านล่าง); หรือหาก<<\EOTหรือ<<'EOT'ใช้งานเอาต์พุตของsshคำสั่งสามารถกำหนดให้REL_DIRถ้าเอาต์พุตเฉพาะของsshคำสั่งไปยัง stdout นั้นได้รับการสร้างขึ้นโดยecho "$REL_DIR"ภายในเอกสาร escaped / single- quote ที่นี่ ( รุ่น 2ด้านล่าง)

ตัวเลือกที่สามคือการเก็บเอกสารที่นี่ในตัวแปรแล้วส่งตัวแปรนี้เป็นอาร์กิวเมนต์คำสั่งไปที่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 สิ่งนี้ช่วยให้ฉันเข้าใจจริง ๆ มากกว่าแค่หลีกเลี่ยงมัน
ด่าน

29

ข้อมูลที่เกี่ยวข้องทั้งหมดอยู่ในคำตอบที่มีอยู่ แต่ให้ฉันลองสรุปในทางปฏิบัติ :

TL; DR:

  • ทำการส่งคำสั่งเพื่อรันโดยใช้อาร์กิวเมนต์บรรทัดคำสั่ง :
    ssh jdoe@server '...'

    • '...' สตริงสามารถขยายได้หลายบรรทัดดังนั้นคุณสามารถให้โค้ดของคุณอ่านได้โดยไม่ต้องใช้เอกสารที่นี่:
      ssh jdoe@server ' ... '
  • อย่าส่งคำสั่งผ่าน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 (pseudo-terminal) โดยค่าเริ่มต้นยกเว้นว่า stdin นั้นไม่ได้เชื่อมต่อกับเทอร์มินัล (ของจริง)

    • การส่งคำสั่งผ่าน stdin หมายความว่าsshstdin นั้นไม่ได้เชื่อมต่อกับเทอร์มินัลอีกต่อไปดังนั้นจึงไม่มีการสร้าง 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
"เพิ่มตัวเลือก -t เป็นสองเท่าเพื่อบังคับให้สร้าง pty: ssh -t -t ... หรือ ssh -tt ... แสดงให้เห็นว่าคุณหมายถึงมันจริงๆ" - ฮ่า ๆ ๆ - ขอบคุณมันตลก แต่จริงจังด้วย - ฉันไม่สามารถเอาหัวของฉันไปรอบ ๆ สิ่งที่ฉันรู้ก็คือถ้าฉันใส่มันทีเดียวมันก็ไม่ได้
แย่

22

ฉันไม่ทราบว่าแฮงค์มาจากไหน แต่การเปลี่ยนเส้นทาง (หรือการไพพ์) คำสั่งไปยัง ssh แบบอินเทอร์แอคทีฟเป็นสูตรสำหรับปัญหาทั่วไป มันมีประสิทธิภาพมากกว่าที่จะใช้สไตล์ command-to-run-as-a-last-อาร์กิวเมนต์และส่งสคริปต์บนบรรทัดคำสั่ง 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'

(ทั้งหมดใน'อาร์กิวเมนต์บรรทัดรับคำสั่งหลายบรรทัดขนาดยักษ์)

ข้อความหลอกเทอร์มินัลเป็นเพราะของคุณ-tซึ่งขอให้ ssh พยายามทำให้สภาพแวดล้อมมันทำงานบนเครื่องระยะไกลมีลักษณะเหมือนขั้วที่แท้จริงไปยังโปรแกรมที่ทำงานที่นั่น ไคลเอ็นต์ ssh ของคุณปฏิเสธที่จะทำเช่นนั้นเนื่องจากอินพุตมาตรฐานของตัวเองไม่ใช่เทอร์มินัลดังนั้นจึงไม่มีวิธีใดที่จะส่งผ่านเทอร์มินัลพิเศษ API เป็นต้นไปจากเครื่องระยะไกลไปยังเครื่องปลายทางที่แท้จริงของคุณ

สิ่งที่คุณกำลังพยายามที่จะบรรลุด้วย-tหรือไม่?


1
ตัวเลือก -t เป็นความพยายามในการแก้ไขปัญหาเทอร์มินัล Psuedo (มันไม่ทำงาน) ฉันลองวิธีแก้ปัญหาของคุณแล้วมันก็กำจัดสิ่งที่เทอร์มินัลของ psuedo แต่ตอนนี้มันเพิ่งจะหยุด ...
Matthew

4
@ Henning: คุณช่วยอธิบายรายละเอียดหรือให้ลิงค์เกี่ยวกับข้อเสียของการเปลี่ยนเส้นทางหรือ piping เป็น ssh แบบโต้ตอบได้หรือไม่?
Isaac Kleinman

สายไปหน่อย แต่: ที่นี่คุณไม่จำเป็นต้องใช้เทอร์มินัลเทียมหลอกดังนั้นให้ใช้ตัวเลือก-Tแทน
Florian Castellane

6

หลังจากอ่านคำตอบเหล่านี้จำนวนมากฉันคิดว่าฉันจะแบ่งปันวิธีแก้ปัญหาที่เกิดขึ้น ทั้งหมดที่ฉันเพิ่มคือ/bin/bashก่อนที่จะ heredoc และจะไม่ให้ข้อผิดพลาดอีกต่อไป

ใช้สิ่งนี้:

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 คุณต้องติดตั้ง plink.exe ของ PuTTY เพื่อให้สามารถใช้งานได้

สารละลาย

  1. Mx ปรับแต่งตัวแปร (แล้วกด Enter)
  2. tramp-default-method (จากนั้นกด Enter อีกครั้ง)
  3. บนฟิลด์ข้อความใส่ plink แล้วนำไปใช้และบันทึกบัฟเฟอร์
  4. เมื่อใดก็ตามที่ฉันพยายามเข้าถึงเซิร์ฟเวอร์ระยะไกลตอนนี้ฉันใช้ Cxf / user @ host: จากนั้นป้อนรหัสผ่าน ตอนนี้ทำการเชื่อมต่ออย่างถูกต้องภายใต้ Emacs บน Windows ไปยังเซิร์ฟเวอร์ระยะไกลของฉัน

-3

ssh -t foobar @ localhost yourscript.pl


2
OP ใช้-tเช่นกัน แต่ในสถานการณ์เฉพาะของพวกเขานั้นยังไม่เพียงพอซึ่งทำให้คำถามเริ่มต้นด้วย
mklement0
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.