Herhangi bir uygulamanın sshbir istemciden sunucuya bir kabuk eklemeden komut iletmenin doğal bir yolu olduğunu sanmıyorum .
Şimdi, uzaktaki kabuğa yalnızca belirli bir tercüman çalıştırmasını söylerseniz ( shbeklenen sözdizimini bildiğimiz gibi ) ve başka bir yöntemle yürütmek için kod vermeniz durumunda işler daha kolay olabilir .
Bu diğer ortalama, örneğin standart girdi veya bir ortam değişkeni olabilir .
Hiçbiri kullanılamadığında, aşağıda üçüncü bir hack çözümü öneririm.
Stdin kullanımı
Herhangi bir veriyi uzak komuta beslemeniz gerekmiyorsa, bu en kolay çözümdür.
Uzak ana makinenin seçeneği xargsdestekleyen bir komutu olduğunu -0ve komutun çok büyük olmadığını biliyorsanız, şunları yapabilirsiniz:
printf '%s\0' "${cmd[@]}" | ssh user@host 'xargs -0 env --'
Bu xargs -0 env --komut satırı, tüm bu kabuk aileleriyle aynı şekilde yorumlanır. xargsstdin'deki boş sınırlandırılmış argümanlar listesini okur ve bunları argümanlar olarak iletir env. Bu, ilk argümanın (komut adı) =karakter içermediğini varsayar .
Veya shher bir öğeyi shalıntı sözdizimini kullanarak alıntı yaptıktan sonra uzak ana bilgisayarda kullanabilirsiniz .
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
Ortam değişkenlerini kullanma
Şimdi, istemciden bazı verileri uzak komutun stdinine beslemeniz gerekiyorsa, yukarıdaki çözüm çalışmaz.
sshAncak bazı sunucu dağıtımları, isteğe bağlı ortam değişkenlerinin istemciden sunucuya iletilmesine izin verir. Örneğin, Debian tabanlı sistemlerde birçok openssh uygulaması, adı ile başlayan değişkenlere izin verir LC_.
Bu durumlarda LC_CODE, örneğin yukarıda belirtilen shquoted sh kodunu içeren bir değişkene sahip olabilir ve sh -c 'eval "$LC_CODE"'istemcinize bu değişkeni iletmesini söyledikten sonra uzak ana bilgisayarda çalıştırılabilir (yine, her kabukta aynı şekilde yorumlanan bir komut satırı):
LC_CODE=$(shquote "${cmd[@]}") ssh -o SendEnv=LC_CODE user@host '
sh -c '\''eval "$LC_CODE"'\'
Tüm kabuk aileleriyle uyumlu bir komut satırı oluşturma
Yukarıdaki seçeneklerden hiçbiri kabul edilemezse (stdin'e ve sshd'ye herhangi bir değişkeni kabul etmediğinizden veya genel bir çözüme ihtiyaç duyduğunuzdan dolayı), o zaman tümüyle uyumlu olan uzak ana bilgisayar için bir komut satırı hazırlamanız gerekir. desteklenen mermiler.
Bu özellikle aldatıcıdır çünkü tüm bu kabukların (Bourne, csh, rc, es, balık) kendi farklı sözdizimleri vardır ve özellikle farklı alıntılama mekanizmaları vardır ve bazılarının çalışması zor olan sınırlamalara sahiptir.
İşte geldiğim bir çözüm, daha ayrıntılı olarak anlatacağım:
#! /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;
Bu perletrafındaki bir sarmalayıcı betiği ssh. Ben ararım sexec. Buna şöyle diyorsun:
sexec [ssh-options] user@host -- cmd and its args
yani örneğinde:
sexec user@host -- "${cmd[@]}"
Ve sarmalayıcı cmd and its args, tüm kabukların cmd, argümanları ile çağırdığı şeklinde yorum yapar (içeriğinden bağımsız olarak).
Sınırlamalar:
- Giriş ve komutun alıntı şekli, uzak komut satırının önemli ölçüde daha büyük olduğu anlamına gelir, bu da komut satırının maksimum büyüklüğündeki sınıra daha erken ulaşılması anlamına gelir.
- Sadece şunu test ettim: Bourne kabuğu (yadigarı toolchest'ten), kısa çizgi, bash, zsh, mksh, lksh, yash, ksh93, rc, es, akanga, csh, tcsh, yeni bir Debian sisteminde bulunan balık ve / Solaris 10'daki bin / sh, / usr / bin / ksh, / bin / csh ve / usr / xpg4 / bin / sh.
- Eğer
yashuzaktan giriş kabuğu, kimin argümanlar geçersiz karakterler içeren bir komut geçemez, ancak bunda bir sınırlama var yashzaten geçici bir çözüm olamayacağını.
- Csh veya bash gibi bazı kabuklar, ssh üzerinden çağrıldığında bazı başlangıç dosyalarını okurlar. Bunların davranışı dramatik biçimde değiştirmediğini ve giriş kısmın hala çalışacağını varsayıyoruz.
- yanında
sh, uzaktaki sistemin de printfkomutu olduğunu varsayar .
Nasıl çalıştığını anlamak için, farklı kabuklarda alıntılamanın nasıl çalıştığını bilmeniz gerekir:
- Bourne: İçinde özel bir karakter bulunmayan
'...' güçlü tırnaklardır . ters eğik çizgiyle kaçılabilecek zayıf tırnaklardır ."...""
csh. Bourne ile aynı, ancak "içeri giremez "...". Ayrıca bir ters eğik çizgi ile önceden eklenmiş bir yeni satır karakteri girilmelidir. Ve !tek tırnak içinde bile sorunlara neden olur.
rc. Tek tırnak '...'(güçlü). Tek tırnak içinde tek bir alıntı ''(gibi '...''...') olarak girilir . Çift tırnak veya ters eğik çizgi özel değildir.
es. Dış tırnak işaretleri dışında rc ile aynı ters eğik çizgi tek bir alıntıdan kaçabilir.
fish: 'içeride ters eğik çizgi kaçması dışında Bourne ile aynı '...'.
Tüm bu çelişkilerde, birinin tüm satırlarda çalışabilmesi için komut satırı argümanlarını güvenilir bir şekilde alıntılayamayacağını görmek kolaydır.
Tek tırnakların aşağıdaki gibi kullanılması:
'foo' 'bar'
hepsinde çalışır ama:
'echo' 'It'\''s'
işe yaramazdı rc.
'echo' 'foo
bar'
işe yaramazdı csh.
'echo' 'foo\'
işe yaramazdı fish.
Biz ters eğik çizgi gibi değişkenlerde bu sorunlu karakterleri depolamak için yönetmek Ancak biz etrafındaki en bu sorunların çalışmak gerekir $b, tek alıntı $qiçinde, yeni satır $n(ve !de $xbir kabuk bağımsız bir şekilde csh geçmişi genişleme).
'echo' 'It'$q's'
'echo' 'foo'$b
her mermide çalışırdı. Yine de bu newline için işe yaramaz csh. Eğer $nnewline içeriyorsa, içine newline genişletecek şekilde cshyazmanız gerekir $n:qve bu, diğer mermiler için işe yaramaz. Öyleyse, burada yaptığımız şey, bunun yerine çağırmak shve shgenişletmektir $n. Bu aynı zamanda, bir tanesi uzak oturum açma kabuğu için diğeri için olmak üzere iki alıntı seviyesinin yapılması gerektiği anlamına gelir sh.
Bu $preamblekodun en zor kısmı. Sadece bu tanımlayan her biri (diğerleri için dışarı yorumladı esnada) kabukları sadece biri tarafından yorumlanır kod bazı bölümleri için tüm kabuklarda çeşitli alıntı kurallardan yararlanır $b, $q, $n, $xkendi kabuğunda için değişkenler.
hostÖrnek olarak uzaktaki kullanıcının giriş kabuğu tarafından yorumlanacak olan kabuk kodu :
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
Bu kod, desteklenen mermilerden herhangi biri tarafından yorumlandığında aynı komutu çalıştırır.
cmdargüman/bin/sh -ctüm vakaların% 99'unda bir posix kabuğu ile sonuçlanırdı, değil mi? Elbette özel karakterlerden kaçmak bu şekilde biraz daha acı vericidir, ancak ilk sorunu çözer mi?