ฉันไม่คิดว่าการติดตั้งใด ๆssh
มีวิธีเนทีฟในการส่งคำสั่งจากไคลเอ็นต์ไปยังเซิร์ฟเวอร์โดยไม่ต้องใช้เชลล์
ตอนนี้สิ่งต่าง ๆ สามารถทำได้ง่ายขึ้นถ้าคุณสามารถบอกให้รีโมตเชลล์ทำงานเฉพาะล่ามที่เฉพาะเจาะจง (เช่นsh
ที่เรารู้จักไวยากรณ์ที่คาดหมาย) และให้โค้ดเพื่อรันโดยค่าเฉลี่ยอื่น
ที่มีความหมายอื่น ๆ สามารถเช่นเข้ามาตรฐานหรือตัวแปรสภาพแวดล้อม
เมื่อไม่สามารถใช้งานได้ฉันขอเสนอวิธีแก้ปัญหาที่สามด้านล่าง
ใช้ stdin
หากคุณไม่จำเป็นต้องป้อนข้อมูลใด ๆ ไปยังคำสั่งระยะไกลนั่นเป็นทางออกที่ง่ายที่สุด
หากคุณรู้ว่ารีโมตโฮสต์มีxargs
คำสั่งที่สนับสนุน-0
ตัวเลือกและคำสั่งนั้นไม่ใหญ่เกินไปคุณสามารถทำได้:
printf '%s\0' "${cmd[@]}" | ssh user@host 'xargs -0 env --'
ที่xargs -0 env --
บรรทัดคำสั่งถูกตีความเดียวกันกับทุกคนในครอบครัวเปลือก xargs
อ่านรายการ null จุลภาคของข้อโต้แย้งใน stdin env
และผ่านเหล่านั้นเป็นข้อโต้แย้ง ซึ่งถือว่าอาร์กิวเมนต์แรก (ชื่อคำสั่ง) ไม่มี=
อักขระ
หรือคุณสามารถใช้sh
บนรีโมตโฮสต์หลังจากอ้างถึงแต่ละองค์ประกอบโดยใช้sh
ไวยากรณ์การอ้างอิง
shquote() {
LC_ALL=C awk -v q=\' '
BEGIN{
for (i=1; i<ARGC; i++) {
gsub(q, q "\\" q q, ARGV[i])
printf "%s ", q ARGV[i] q
}
print ""
}' "$@"
}
shquote "${cmd[@]}" | ssh user@host sh
การใช้ตัวแปรสภาพแวดล้อม
ตอนนี้ถ้าคุณจำเป็นต้องป้อนข้อมูลบางอย่างจากไคลเอนต์ไปยัง stdin ของคำสั่งระยะไกลการแก้ปัญหาข้างต้นจะไม่ทำงาน
ssh
การปรับใช้เซิร์ฟเวอร์บางอย่างอย่างไรก็ตามอนุญาตให้ส่งผ่านตัวแปรสภาพแวดล้อมโดยพลการจากไคลเอนต์ไปยังเซิร์ฟเวอร์ ยกตัวอย่างเช่นระบบการใช้งานหลาย openssh ใน Debian LC_
ตามอนุญาตให้ผ่านตัวแปรที่มีชื่อเริ่มต้นด้วย
ในกรณีดังกล่าวคุณอาจมีLC_CODE
ตัวแปรเช่นที่มีโค้ดshquoted sh
ข้างต้นและทำงานsh -c 'eval "$LC_CODE"'
บนรีโมตโฮสต์หลังจากบอกให้ไคลเอ็นต์ของคุณผ่านตัวแปรนั้น (อีกครั้งนั่นคือบรรทัดคำสั่งที่ตีความเหมือนกันในทุกเชลล์):
LC_CODE=$(shquote "${cmd[@]}") ssh -o SendEnv=LC_CODE user@host '
sh -c '\''eval "$LC_CODE"'\'
การสร้างบรรทัดคำสั่งที่เข้ากันได้กับตระกูลเชลล์ทั้งหมด
หากไม่มีตัวเลือกใด ๆ ข้างต้นเป็นที่ยอมรับได้ (เพราะคุณต้องการ stdin และ sshd ไม่ยอมรับตัวแปรใด ๆ หรือเพราะคุณต้องการโซลูชันทั่วไป) คุณจะต้องเตรียมบรรทัดคำสั่งสำหรับโฮสต์ระยะไกลที่เข้ากันได้กับทุกคน หอยที่รองรับ
นั่นเป็นเรื่องยากโดยเฉพาะอย่างยิ่งเพราะกระสุนเหล่านั้น (Bourne, csh, rc, es, fish) มีรูปแบบที่แตกต่างกันของตัวเองและโดยเฉพาะอย่างยิ่งกลไกการอ้างอิงที่แตกต่างกันและบางส่วนมีข้อ จำกัด ที่ยากต่อการแก้ไข
นี่เป็นวิธีแก้ปัญหาที่ฉันคิดขึ้นฉันอธิบายต่อไป:
#! /usr/bin/perl
my $arg, @ssh, $preamble =
q{printf '%.0s' "'\";set x=\! b=\\\\;setenv n "\
";set q=\';printf %.0s "\""'"';q='''';n=``()echo;x=!;b='\'
printf '%.0s' '\'';set b \\\\;set x !;set -x n \n;set q \'
printf '%.0s' '\'' #'"\"'";export n;x=!;b=\\\\;IFS=.;set `echo;echo \.`;n=$1 IFS= q=\'
};
@ssh = ('ssh');
while ($arg = shift @ARGV and $arg ne '--') {
push @ssh, $arg;
}
if (@ARGV) {
for (@ARGV) {
s/'/'\$q\$b\$q\$q'/g;
s/\n/'\$q'\$n'\$q'/g;
s/!/'\$x'/g;
s/\\/'\$b'/g;
$_ = "\$q'$_'\$q";
}
push @ssh, "${preamble}exec sh -c 'IFS=;exec '" . join "' '", @ARGV;
}
exec @ssh;
นั่นเป็นรอบสคริปต์เสื้อคลุมperl
ผมเรียกมันว่าssh
sexec
คุณเรียกว่าชอบ:
sexec [ssh-options] user@host -- cmd and its args
ดังนั้นในตัวอย่างของคุณ:
sexec user@host -- "${cmd[@]}"
และ wrapper เปลี่ยนcmd and its args
เป็นบรรทัดคำสั่งที่เชลล์ทั้งหมดจบการตีความเป็นการเรียกcmd
ด้วย args (ไม่นับเนื้อหาของมัน)
ข้อ จำกัด :
- คำนำและวิธีการอ้างอิงคำสั่งหมายถึงบรรทัดคำสั่งรีโมตจะใหญ่ขึ้นอย่างมีนัยสำคัญซึ่งหมายความว่าจะมีการ จำกัด ขนาดสูงสุดของบรรทัดคำสั่งในไม่ช้า
- ฉันได้ทดสอบด้วย: Bourne shell (จากมรดกสืบทอด), ประ, ทุบตี, zsh, mksh, lksh, yash, ksh93, rc, es, akanga, csh, tcsh, ปลาที่พบในระบบ Debian ล่าสุดและ / bin / sh, / usr / bin / ksh, / bin / csh และ / usr / xpg4 / bin / sh บน Solaris 10
- หาก
yash
เป็นเชลล์การเข้าสู่ระบบระยะไกลคุณจะไม่สามารถส่งคำสั่งที่อาร์กิวเมนต์มีอักขระที่ไม่ถูกต้อง แต่นั่นเป็นข้อ จำกัดyash
ที่คุณไม่สามารถแก้ไขได้
- เชลล์บางตัวเช่น csh หรือ bash อ่านไฟล์เริ่มต้นบางไฟล์เมื่อเรียกใช้ผ่าน ssh เราคิดว่าสิ่งเหล่านั้นจะไม่เปลี่ยนพฤติกรรมอย่างมากเพื่อให้การเริ่มนำยังคงใช้ได้
- ข้าง
sh
ก็ยังถือว่าระบบจากระยะไกลมีprintf
คำสั่ง
เพื่อให้เข้าใจถึงวิธีการทำงานคุณต้องรู้ว่าการอ้างอิงทำงานอย่างไรในเชลล์ที่แตกต่างกัน:
- Bourne:
'...'
เป็นคำพูดที่แข็งแกร่งโดยไม่มีตัวอักษรพิเศษอยู่ในนั้น "..."
เป็นเครื่องหมายคำพูดอ่อนแอที่"
สามารถใช้เครื่องหมายแบคสแลชได้
csh
. เหมือนกับ Bourne ยกเว้นว่า"
ไม่สามารถหลบหนี"..."
ได้ นอกจากนี้ยังต้องป้อนอักขระขึ้นบรรทัดใหม่ด้วยเครื่องหมายแบ็กสแลช และ!
ทำให้เกิดปัญหาแม้ในเครื่องหมายคำพูดเดี่ยว
rc
. เครื่องหมายคำพูดเท่านั้น'...'
(แข็งแรง) เครื่องหมายคำพูดเดี่ยวในเครื่องหมายคำพูดเดียวถูกป้อนเป็น''
(ชอบ'...''...'
) เครื่องหมายคำพูดหรือแบ็กสแลชสองชั้นไม่ได้พิเศษ
es
. เช่นเดียวกับ rc ยกเว้นว่าเครื่องหมายอัญประกาศด้านนอกแบ็กสแลชสามารถหนีจากเครื่องหมายคำพูดเดี่ยวได้
fish
: เช่นเดียวกับบอร์นยกเว้นทับขวาที่หนีภายใน'
'...'
ด้วยข้อ จำกัด เหล่านั้นมันง่ายที่จะเห็นว่าไม่มีใครสามารถอ้างอิงอาร์กิวเมนต์บรรทัดคำสั่งได้อย่างน่าเชื่อถือเพื่อให้ทำงานได้กับเชลล์ทั้งหมด
ใช้คำพูดเดียวใน:
'foo' 'bar'
ทำงานได้ทั้งหมด แต่:
'echo' 'It'\''s'
rc
จะไม่ทำงานใน
'echo' 'foo
bar'
csh
จะไม่ทำงานใน
'echo' 'foo\'
fish
จะไม่ทำงานใน
อย่างไรก็ตามเราควรจะสามารถแก้ไขปัญหาเหล่านั้นได้หากเราจัดการเก็บอักขระที่มีปัญหาเหล่านั้นในตัวแปรเช่นแบ็กสแลชในเครื่องหมาย$b
คำพูดเดี่ยว$q
บรรทัดใหม่ใน$n
(และ!
ใน$x
การขยายประวัติ csh) ด้วยวิธีอิสระเชลล์
'echo' 'It'$q's'
'echo' 'foo'$b
จะใช้ได้กับทุกเชลล์ ที่ยังคงไม่ทำงานสำหรับการขึ้นบรรทัดใหม่สำหรับcsh
แม้ว่า หาก$n
มีการขึ้นบรรทัดใหม่csh
คุณต้องเขียน$n:q
มันเพื่อขยายไปยังการขึ้นบรรทัดใหม่และนั่นจะไม่ทำงานกับเชลล์ตัวอื่น ดังนั้นสิ่งที่เราสิ้นสุดการทำแทนที่นี่จะเรียกsh
และมีการขยายเหล่านั้นsh
$n
นั่นหมายความว่าต้องทำการอ้างอิงสองระดับหนึ่งระดับสำหรับเชลล์ล็อกอินระยะไกลและอีกหนึ่งsh
ระดับ
$preamble
ในรหัสที่เป็นส่วนหนึ่งที่ยาก ทำให้การใช้กฎข้อความต่างๆที่แตกต่างกันในเปลือกหอยทุกคนที่จะมีบางส่วนของรหัสตีความโดยเพียงหนึ่งของเปลือกหอย (ในขณะที่มันแสดงความคิดเห็นออกมาให้คนอื่น ๆ ) ซึ่งแต่ละเพียงการกำหนดผู้ที่$b
, $q
, $n
, $x
ตัวแปรเปลือกของตน
นี่คือรหัสเชลล์ที่จะถูกตีความโดยล็อกอินเชลล์ของผู้ใช้รีโมตบนhost
สำหรับตัวอย่างของคุณ:
printf '%.0s' "'\";set x=\! b=\\;setenv n "\
";set q=\';printf %.0s "\""'"';q='''';n=``()echo;x=!;b='\'
printf '%.0s' '\'';set b \\;set x !;set -x n \n;set q \'
printf '%.0s' '\'' #'"\"'";export n;x=!;b=\\;IFS=.;set `echo;echo \.`;n=$1 IFS= q=\'
exec sh -c 'IFS=;exec '$q'printf'$q' '$q'<%s>'$b'n'$q' '$q'arg with $and spaces'$q' '$q''$q' '$q'even'$q'$n'$q'* * *'$q'$n'$q'newlines'$q' '$q'and '$q$b$q$q'single quotes'$q$b$q$q''$q' '$q''$x''$x''$q
รหัสนั้นจะจบลงด้วยการรันคำสั่งเดียวกันเมื่อตีความโดยเชลล์ที่สนับสนุนใด ๆ
cmd
โต้เถียงคือ/bin/sh -c
เราจะจบลงด้วยเปลือก posix ใน 99% ของทุกกรณีเราจะ? แน่นอนว่าการหลีกเลี่ยงตัวละครพิเศษมันเจ็บปวดมากกว่านี้เล็กน้อย แต่มันจะแก้ปัญหาเบื้องต้นได้ไหม?