Herhangi bir uygulamanın ssh
bir 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 ( sh
beklenen 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 xargs
destekleyen bir komutu olduğunu -0
ve 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. xargs
stdin'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 sh
her bir öğeyi sh
alı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.
ssh
Ancak 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 perl
etrafı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
yash
uzaktan giriş kabuğu, kimin argümanlar geçersiz karakterler içeren bir komut geçemez, ancak bunda bir sınırlama var yash
zaten 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 printf
komutu 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ı $q
içinde, yeni satır $n
(ve !
de $x
bir 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 $n
newline içeriyorsa, içine newline genişletecek şekilde csh
yazmanız gerekir $n:q
ve bu, diğer mermiler için işe yaramaz. Öyleyse, burada yaptığımız şey, bunun yerine çağırmak sh
ve sh
geniş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 $preamble
kodun 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
, $x
kendi 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.
cmd
argüman/bin/sh -c
tü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?