TL; DR
-t
อย่าใช้ -t
เกี่ยวข้องกับเทอร์มินัลหลอกบนรีโมตโฮสต์และควรใช้เพื่อรันแอปพลิเคชันแบบเห็นภาพจากเทอร์มินัลเท่านั้น
คำอธิบาย
อักขระ linefeed (หรือที่รู้จักในชื่อ newline หรือ\n
) คืออักขระที่เมื่อส่งไปยังเทอร์มินัลจะบอกให้เทอร์มินัลเลื่อนเคอร์เซอร์ลง
แต่เมื่อคุณเรียกใช้seq 3
ในเทอร์มินัลนั่นคือที่ที่seq
เขียน1\n2\n3\n
ไปยังสิ่งที่ชอบ/dev/pts/0
คุณไม่เห็น:
1
2
3
แต่
1
2
3
ทำไมถึงเป็นอย่างนั้น?
อันที่จริงเมื่อseq 3
(หรือssh host seq 3
สำหรับเรื่องที่) เขียน1\n2\n3\n
, terminal 1\r\n2\r\n3\r\n
เห็น นั่นคือฟีดบรรทัดได้รับการแปลเป็น carriage-return (ซึ่งเทอร์มินัลย้ายเคอร์เซอร์ของพวกเขากลับไปทางซ้ายของหน้าจอ) และฟีดบรรทัด
ที่ทำโดยไดรเวอร์อุปกรณ์ปลายทาง ยิ่งไปกว่านั้นโดยบรรทัดการลงวินัยของอุปกรณ์เทอร์มินัล (หรือสถานีหลอก) โมดูลซอฟต์แวร์ที่อยู่ในเคอร์เนล
คุณสามารถควบคุมพฤติกรรมของวินัยในบรรทัดนั้นด้วยstty
คำสั่ง การแปลของLF
-> CRLF
เปิดด้วย
stty onlcr
(ซึ่งโดยทั่วไปจะเปิดใช้งานตามค่าเริ่มต้น) คุณสามารถปิดได้ด้วย:
stty -onlcr
หรือคุณสามารถปิดการประมวลผลเอาต์พุตด้วย:
stty -opost
หากคุณทำและเรียกใช้seq 3
คุณจะเห็น:
$ stty -onlcr; seq 3
1
2
3
อย่างที่คาดไว้.
ตอนนี้เมื่อคุณ:
seq 3 > some-file
seq
ไม่ได้เขียนไปยังเทอร์มินัลอีกต่อไป แต่กำลังเขียนลงในไฟล์ไม่มีการแปล ดังนั้นจะมีsome-file
1\n2\n3\n
การแปลเสร็จสิ้นเมื่อเขียนไปยังอุปกรณ์ปลายทางเท่านั้น และจะทำเพื่อการแสดงเท่านั้น
ในทำนองเดียวกันเมื่อคุณ:
ssh host seq 3
ssh
กำลังเขียน1\n2\n3\n
โดยไม่คำนึงถึงสิ่งที่ssh
ส่งออกไป
สิ่งที่เกิดขึ้นจริงคือseq 3
คำสั่งถูกรันhost
โดย stdout เปลี่ยนเส้นทางไปยังไพพ์ ssh
เซิร์ฟเวอร์บนโฮสต์อ่านส่วนอื่น ๆ ของท่อและส่งมันมากกว่าช่องทางเข้ารหัสเพื่อคุณssh
ลูกค้าและssh
ลูกค้าเขียนมันลงบน stdout ของตนในกรณีของคุณอุปกรณ์หลอกขั้วที่LF
s แปลงCRLF
สำหรับการแสดงผล
แอ็พพลิเคชันแบบโต้ตอบจำนวนมากทำงานแตกต่างกันเมื่อ stdout ไม่ใช่เทอร์มินัล ตัวอย่างเช่นหากคุณเรียกใช้:
ssh host vi
vi
ไม่ชอบมันไม่ชอบเอาท์พุทมันจะไปป์ มันคิดว่ามันไม่ได้พูดคุยกับอุปกรณ์ที่สามารถทำความเข้าใจกับลำดับการหลีกตำแหน่งเคอร์เซอร์เช่น
ดังนั้นssh
มี-t
ตัวเลือกสำหรับสิ่งนั้น ด้วยตัวเลือกที่เซิร์ฟเวอร์ SSH บนโฮสต์สร้างอุปกรณ์หลอกขั้วและทำให้ว่า stdout (และ stdin และ stderr) vi
ของ สิ่งที่vi
เขียนบนอุปกรณ์เทอร์มินัลนั้นต้องผ่านระเบียบวินัยการใช้สายปลอมหลอกระยะไกลและถูกอ่านโดยssh
เซิร์ฟเวอร์และส่งผ่านช่องสัญญาณที่เข้ารหัสไปยังssh
ลูกค้า มันเป็นเช่นเดียวกับก่อนยกเว้นว่าแทนการใช้ท่อที่ssh
เซิร์ฟเวอร์ใช้ขั้วหลอก
ข้อแตกต่างอื่น ๆ คือที่ฝั่งssh
ไคลเอ็นต์ไคลเอ็นต์ตั้งค่าเทอร์มินัลในraw
โหมด ซึ่งหมายความว่าจะไม่มีการแปลที่นั่น ( opost
ถูกปิดใช้งานและพฤติกรรมด้านอินพุตอื่น ๆ ) ตัวอย่างเช่นเมื่อคุณพิมพ์Ctrl-Cแทนการขัดจังหวะการssh
ที่^C
ตัวละครถูกส่งไปยังด้านระยะไกลที่มีระเบียบวินัยสายระยะไกลหลอกขั้วส่งขัดจังหวะกับคำสั่งจากระยะไกล
เมื่อคุณทำ:
ssh -t host seq 3
seq 3
เขียน1\n2\n3\n
ลงใน stdout ซึ่งเป็นอุปกรณ์แบบหลอกเทียม เพราะonlcr
นั่นจะได้รับการแปลในโฮสต์เพื่อ1\r\n2\r\n3\r\n
และส่งถึงคุณผ่านช่องทางเข้ารหัส ที่ด้านข้างของคุณไม่มีการแปล ( onlcr
ปิดการใช้งาน) ดังนั้น1\r\n2\r\n3\r\n
จะไม่ถูกแตะต้อง (เนื่องจากraw
โหมด) และถูกต้องบนหน้าจอของเทอร์มินัลอีมูเลเตอร์ของคุณ
ตอนนี้ถ้าคุณทำ:
ssh -t host seq 3 > some-file
ไม่มีความแตกต่างจากด้านบน ssh
จะเขียนสิ่งเดียวกันแต่เวลานี้ลงใน1\r\n2\r\n3\r\n
some-file
ดังนั้นโดยทั่วไปทั้งหมดLF
ในการส่งออกของseq
ได้รับการแปลออกเป็นCRLF
some-file
มันเหมือนกันถ้าคุณ:
ssh -t host cat remote-file > local-file
LF
อักขระทั้งหมด(0x0a ไบต์) กำลังแปลเป็น CRLF (0x0d 0x0a)
นั่นอาจเป็นสาเหตุของความเสียหายในไฟล์ของคุณ ในกรณีของไฟล์ที่มีขนาดเล็กกว่าที่สองมันเกิดขึ้นเมื่อไฟล์ไม่มีขนาด 0x0a ไบต์ดังนั้นจึงไม่มีความเสียหาย
โปรดทราบว่าคุณอาจได้รับความเสียหายประเภทต่าง ๆ ด้วยการตั้งค่า tty ที่แตกต่างกัน ความเป็นไปได้อีกประเภทหนึ่งของความเสียหายที่เกี่ยวข้องกับ-t
คือถ้าไฟล์เริ่มต้นของคุณในhost
( ~/.bashrc
, ~/.ssh/rc
... ) เขียนสิ่งต่าง ๆ ลงใน stderr เนื่องจาก-t
stdout และ stderr ของเชลล์ระยะไกลจบลงด้วยการรวมเข้าเป็นssh
stdout ของพวกเขา - อุปกรณ์ปลายทาง)
คุณไม่ต้องการให้รีโมตcat
ส่งออกไปยังอุปกรณ์ปลายทางที่นั่น
คุณต้องการ:
ssh host cat remote-file > local-file
คุณสามารถทำได้:
ssh -t host 'stty -opost; cat remote-file` > local-file
ที่จะทำงาน (ยกเว้นในการเขียน stderrกรณีการทุจริตที่กล่าวข้างต้น) host
แต่แม้ที่จะย่อยที่ดีที่สุดเท่าที่คุณต้องการได้ว่าชั้นหลอกขั้วที่ไม่จำเป็นที่ทำงานบน
สนุกกว่านี้:
$ ssh localhost echo | od -tx1
0000000 0a
0000001
ตกลง.
$ ssh -t localhost echo | od -tx1
0000000 0d 0a
0000002
LF
แปลเป็น CRLF
$ ssh -t localhost 'stty -opost; echo' | od -tx1
0000000 0a
0000001
ตกลงอีกครั้ง
$ ssh -t localhost 'stty olcuc; echo x'
X
นั่นเป็นอีกรูปแบบหนึ่งของการทำโพสต์โพรเซสซิงที่สามารถทำได้โดยวินัยของสายเทอร์มินัล
$ echo x | ssh -t localhost 'stty -opost; echo' | od -tx1
Pseudo-terminal will not be allocated because stdin is not a terminal.
stty: standard input: Inappropriate ioctl for device
0000000 0a
0000001
ssh
ปฏิเสธที่จะบอกเซิร์ฟเวอร์ให้ใช้เทอร์มินัลหลอกเมื่ออินพุตของตัวเองไม่ใช่เทอร์มินัล คุณสามารถบังคับด้วย-tt
:
$ echo x | ssh -tt localhost 'stty -opost; echo' | od -tx1
0000000 x \r \n \n
0000004
วินัยของสายนั้นทำได้มากขึ้นในด้านอินพุต
ที่นี่echo
ไม่ได้อ่านอินพุตของมันและไม่ได้รับการขอให้ส่งออกx\r\n\n
ดังนั้นสิ่งที่มาจากไหน นั่นคือโลคัลecho
ของเทอร์มินัลหลอกหลอกระยะไกล ( stty echo
) ssh
เซิร์ฟเวอร์ที่มีการให้อาหารx\n
มันอ่านจากลูกค้าไปยังด้านหลักของระยะไกลหลอกขั้ว และสายวินัยของมันสะท้อนกลับมา (ก่อนที่จะstty opost
ถูกเรียกใช้ซึ่งเป็นเหตุผลที่เราเห็นCRLF
และไม่ได้LF
) ไม่ขึ้นอยู่กับว่าแอปพลิเคชันระยะไกลอ่านอะไรจาก stdin หรือไม่
$ (sleep 1; printf '\03') | ssh -tt localhost 'trap "echo ouch" INT; sleep 2'
^Couch
0x3
ตัวละครที่สะท้อนกลับมาเป็น^C
( ^
และC
) เพราะstty echoctl
และเปลือกและการนอนหลับได้รับ SIGINT stty isig
เพราะ
ดังนั้นในขณะที่:
ssh -t host cat remote-file > local-file
ไม่ดีพอ แต่
ssh -tt host 'cat > remote-file' < local-file
การถ่ายโอนไฟล์ด้วยวิธีอื่นนั้นแย่กว่ามาก คุณจะได้รับบาง CR -> แปล LF, แต่ยังมีปัญหากับทุกตัวอักษรพิเศษ ( ^C
, ^Z
, ^D
, ^?
, ^S
... ) และยังห่างไกลcat
จะไม่เห็น EOF เมื่อสิ้นสุดของการlocal-file
ถึงเฉพาะเมื่อ^D
ถูกส่งหลังจาก\r
, \n
หรือคนอื่น^D
ชอบเมื่อทำcat > file
ใน terminal ของคุณ
-t
ตัวเลือกซึ่งแบ่งการถ่ายโอน อย่าใช้-t
หรือ-T
ยกเว้นว่าคุณต้องการเหตุผลพิเศษ ค่าเริ่มต้นใช้งานได้ในกรณีส่วนใหญ่ดังนั้นตัวเลือกเหล่านี้จึงไม่ค่อยจำเป็น