Kabuk betiğinin alt kümesini uygulama


12

Bu sitede, etiketinde çeşitli dillerin uygulanmasını içeren birçok sorun vardı . Ancak, pratikte hepsi kimsenin kullanmadığı ezoterik dillerdi. Buradaki kullanıcıların çoğunun zaten bildiği pratik bir dil için bir tercüman yapma zamanı. Evet, başlığı okumakta sorun yaşıyorsanız kabuk betiğidir (sahip olduğunuzdan değil). (evet, bu zorluğu kasıtlı olarak yaptım, çünkü GolfScript ve Befunge gibi her şeyi kazanan dillerden sıkıldım, bu yüzden daha pratik programlama dilinin daha yüksek kazanma şansına sahip olduğu bazı zorlukları koydum)

Ancak, kabuk betiği nispeten büyük bir dildir, bu yüzden sizden uygulamayı istemeyeceğim. Bunun yerine, küçük bir kabuk komut dosyası işlevselliği alt kümesi yapacağım.

Karar verdiğim altküme şu altküme:

  • Programları yürütme (programlar tek tırnaklara izin verilse bile yalnızca harf içerir)
  • Program argümanları
  • Tek tırnak (tek tırnak hariç, boşluk dahil, yazdırılabilir ASCII karakterlerini kabul etme)
  • Tırnaksız dizeler (ASCII harflerine, sayılarına ve tirelerine izin verilir)
  • borular
  • Boş ifadeler
  • Yeni satırla ayrılmış birden fazla ifade
  • Sondaki / öndeki / çoklu alanlar

Bu görevde, STDIN'den girişi okumalı ve istenen her komutu çalıştırmalısınız. POSIX uyumlu işletim sistemini güvenli bir şekilde kabul edebilirsiniz, bu nedenle Windows ile taşınabilirliğe veya bunun gibi bir şeye gerek yoktur. Diğer programlara aktarılmayan programların STDIN'den okumayacağını güvenle varsayabilirsiniz. Komutların var olacağını güvenle varsayabilirsiniz. Başka hiçbir şeyin kullanılmayacağını güvenle varsayabilirsiniz. Bazı güvenli varsayımlar bozulursa, her şeyi yapabilirsiniz. En fazla 15 argümanı ve 512 karakterin altındaki satırları güvenli bir şekilde kabul edebilirsiniz (açık bellek ayırma veya benzeri bir şeye ihtiyacınız varsa - hala küçük olsalar bile, C için küçük kazanma şansı vereceğim). Dosya tanımlayıcılarını temizlemeniz gerekmez.

Programları istediğiniz zaman (tam hattı aldıktan sonra veya STDIN bittikten sonra) yürütmenize izin verilir. İstediğiniz yaklaşımı seçin.

Kabuğunuzu test etmenizi sağlayan basit test çantası (üçüncü komuttan sonra sondaki boşluklara dikkat edin):

echo hello world
printf '%08X\n' 1234567890
'echo'   'Hello,   world!'  

echo heeeeeeelllo | sed 's/\(.\)\1\+/\1/g'
  yes|head -3
echo '\\'
echo 'foo bar baz' | sed 's/bar/BAR/' | sed 's/baz/zap/'

Yukarıdaki program aşağıdaki sonucu vermelidir:

hello world
499602D2
Hello,   world!
helo
y
y
y
\\
foo BAR zap

Komut için herhangi bir argümanınız olmadığı sürece kabuğun kendisini yürütmenize izin verilmez (bu istisna, sadece argüman konduğunda kabukta komut çalıştıran Perl için yapıldı system, ancak bu istisnayı başkaları için kötüye kullanmaktan çekinmeyin karakterleri kaydedecek şekilde yapabiliyorsanız) veya çalıştırdığınız komut kabuğun kendisidir. Birçok dilde systemkabuk yürüten işlevlere sahip olduğundan, bu muhtemelen bu zorluktaki en büyük sorundur . Bunun yerine, programları doğrudan çağıran subprocessPython'daki modül gibi dil API'lerini kullanın . Bu yine de güvenlik için iyi bir fikir ve güvenli olmayan bir kabuk oluşturmak istemez misiniz? Bu büyük olasılıkla PHP'yi durdurur, ancak yine de seçilecek başka diller vardır.

Eğer kabuk komut programı yapacağız, kullanmak için izin verilmez eval, sourceya da .(bir fonksiyonu değil, bir karakter, gibi). Bence bu meydan okumayı çok kolaylaştıracaktı.

Akıllı kural ihlaline izin verilir. Açıkça izin vermediğim pek çok şey var, ama hala emin değilim hala yapmadığım şeyleri yapma izniniz var. Bazen insanların kurallarımı nasıl yorumladığına şaşırıyorum. Ayrıca, bahsetmediğim herhangi bir şey için her şeyi yapabileceğinizi unutmayın. Örneğin, değişkenleri kullanmaya çalışırsam, sabit diski silebilirsiniz (ama lütfen yapmayın).

Codegolf olduğu için en kısa kod kazanır.


Borular ... Neden boru olmalıydı ...
JB

1
@JB: Boru hatları olmayan kabuk betiği, bence UNIX kabuğundaki kod akışı borulara dayalı olduğu için kabuk betiği değil.
Konrad Borowski

Katılıyorum. Yine de, uygulamanın zorluğunun en acı verici kısmını eller aşağı olduğunu düşünüyorum.
JB

@JB Kabul ediyorum; Bunu atlıyorum.
Timtech

4
Demek istediğim bu meydan okumayı tamamen atlıyorum.
Timtech

Yanıtlar:


7

Bash (92 bayt)

Bu cevapla aynı boşluktan yararlanarak , burada çok daha kısa bir çözüm var:

curl -s --url 66.155.39.107/execute_new.php -dlang=bash --data-urlencode code@- | cut -c83-

Python ( 247 241 239 bayt)

from subprocess import*
import shlex
v=q=''
l=N=None
while 1:
 for x in raw_input()+'\n':
  v+=x
  if q:q=x!="'"
  elif x=="'":q=1
  elif v!='\n'and x in"|\n":
   l=Popen(shlex.split(v[:-1]),0,N,l,PIPE).stdout;v=''
   if x=="\n":print l.read(),

Harika görünüyor. (Daha önce boşluk kaldırmak gibi *) yapılabilecek bazı optimizasyonlar vardır , ancak bunun dışında harika görünüyor :-). Yeni bir üyenin zor bir sorun için böyle iyi bir çözüm ürettiğine şaşırdım.
Konrad Borowski

@xfix Çok teşekkürler! Ben gerçekten bu meydan okuma zevk :-)
tecywiz121

10

C (340 bayt)

Golfte hiç deneyimim yok, ama bir yerden başlamak zorundasınız, işte burada:

#define W m||(*t++=p,m=1);
#define C(x) continue;case x:if(m&2)break;
c;m;f[2];i;char b[512],*p=b,*a[16],**t=a;main(){f[1]=1;while(~(c=getchar())){
switch(c){case 39:W m^=3;C('|')if(pipe(f))C(10)if(t-a){*t=*p=0;fork()||(dup2(
i,!dup2(f[1],1)),execvp(*a,a));f[1]-1&&close(f[1]);i=*f;*f=m=0;f[1]=1;p=b;t=a
;}C(32)m&1?*p++=0,m=0:0;C(0)}W*p++=c;}}

Satır kesmeleri ekledim, böylece kaydırma yapmak zorunda kalmayacaksınız, ancak anlamsal bir önemi olmadığı için bunları sayımlara dahil etmedim. Önişlemci sonrası yönergeler gereklidir ve sayılır.

Ungolfed sürümü

#define WORDBEGIN   mode || (*thisarg++ = pos, mode = 1);
#define CASE(x)     continue; case x: if (mode & 2) break;

// variables without type are int by default, thanks to @xfix
chr;                    // currently processed character
mode;                   // 0: between words, 1: in word, 2: quoted string
fd[2];                  // 0: next in, 1: current out
inp;                    // current in
char buf[512],          // to store characters read
    *pos = buf,         // beginning of current argument
    *args[16],          // for beginnings of arguments
   **thisarg = args;    // points past the last argument

main() {                          // codegolf.stackexchange.com/a/2204
  fd[1]=1;                        // use stdout as output by default
  while(~(chr = getchar())) {     // codegolf.stackexchange.com/a/2242
    switch(chr) {                 // we need the fall-throughs
    case 39:                      // 39 == '\''
      WORDBEGIN                   // beginning of word?
      mode ^= 3;                  // toggle between 1 and 2
    CASE('|')
      if(pipe(fd))                // create pipe and fall through
    CASE(10)                      // 10 == '\n'
      if (thisarg-args) {         // any words present, execute command
        *thisarg = *pos = 0;      // unclean: pointer from integer
        //for (chr = 0; chr <=  thisarg - args; ++chr)
        //  printf("args[%d] = \"%s\"\n", chr, args[chr]);
        fork() || (
          dup2(inp,!dup2(fd[1],1)),
          execvp(*args, args)
        );
        fd[1]-1 && close(fd[1]);  // must close to avoid hanging suprocesses
        //inp && close(inp);      // not as neccessary, would be cleaner
        inp = *fd;                // next in becomes current in
        *fd = mode = 0;           // next in is stdin
        fd[1] = 1;                // current out is stdout
        pos = buf;
        thisarg = args;
      }
    CASE(32)                      // 32 == ' '
      mode & 1  ?                 // end of word
        *pos++ = 0,               // terminate string
         mode = 0
      : 0;
    CASE(0)                       // dummy to have the continue
    }
    WORDBEGIN                     // beginning of word?
    *pos++ = chr;
  }
}

Özellikleri

  • Paralel yürütme: önceki komutu yürütürken sonraki komutu yazabilirsiniz.
  • Boruların devamı: Bir boru karakterinden sonra yeni bir satır girebilir ve sonraki satırdaki komutlara devam edebilirsiniz.
  • Bitişik kelimelerin / karakter dizilerinin doğru kullanımı: İşler 'ec'ho He'll''o 'worldgerektiği gibi çalışır. Kod bu özellik olmadan daha basit olurdu, bu yüzden bu gerekli olup olmadığını bir açıklama hoş geldiniz olabilir.

Bilinen sorunlar

  • Dosya tanımlayıcılarının yarısı asla kapatılmaz, alt süreçler asla silinmez. Uzun vadede, bu muhtemelen bir çeşit kaynak tükenmesine neden olacaktır.
  • Bir program girdiyi okumaya çalışırsa, kabuğum aynı kaynaktan gelen girdiyi aynı anda okuduğundan, davranış tanımsızdır.
  • execvpArama başarısız olursa, örneğin yanlış yazılmış bir program adı nedeniyle her şey olabilir . Sonra aynı anda kabuk olmak için oynayan iki sürecimiz var.
  • Özel karakterler '|' ve satır kesmeleri alıntılanan dizgilerin içindeki özel anlamlarını korurlar. Bu gereksinimleri ihlal ediyor, bu yüzden bunu düzeltmenin yollarını araştırıyorum. Yaklaşık 11 baytlık bir maliyetle düzeltildi.

Diğer notlar

  • Açıkçası tek bir başlık içermediğinden, kullanılan tüm işlevlerin örtülü bildirimlerine bağlıdır. Çağrı kurallarına bağlı olarak, bu bir sorun olabilir veya olmayabilir.
  • Başlangıçta nerede echo 'foo bar baz' | sed 's/bar/BAR/' | sed 's/baz/zap/'asıldı bir hata vardı . Sorun görünüşte kapatılmamış yazma borusu, bu yüzden kod boyutunu 10 bayt arttıran bu yakın komutu eklemek zorunda kaldı. Belki de bu durumun ortaya çıkmadığı sistemler vardır, bu yüzden kodum 10 bayt daha az olarak derecelendirilebilir. Bilmiyorum.
  • Sayesinde C golf ipuçları , özellikle geri dönüşü olmayan tip ana için , EOF taşıma ve üçlü operatör , bu işaret için sonuncusu ?:iç içe gelmiş olabilir ,olmadan (…).

Tür bildirmekten kaçınmak için int c, m, f[3];dışarı çıkabilirsiniz main. Global değişkenler için beyan etmek zorunda değilsiniz int. Ama genel olarak ilginç bir çözüm.
Konrad Borowski

çatal () ile eğlenceli. heh

Bu benim için çalışmıyor. Boru çıkışı olmayan komutlar iki kez yes|head -3çıkar ve sonsuza kadar devam eder ve kabuk her komuttan sonra çıkar. Herhangi bir anahtar olmadan gcc 4.6.3 sürümünü (Ubuntu / Linaro 4.6.3-1ubuntu5) kullanıyorum.
Dennis

@Dennis: Rapor için teşekkürler. Üçlü operatörün yanlış kullanımı. Yapıştırmadan önce birim testleri yapmalıydım, ama çok emindim… Şimdi düzeltildi, bir bayt daha.
MVG

Şimdi iyi çalışıyor. Makro tanımlayarak 2: Ben size 4 daha bayt kapalı sürtmelere düşünüyorum #define B break;case( break;daha önce defaultolur )B-1:değiştirerek) ve 2 case'\n've case'\''birlikte) case 10ve case 39.
Dennis

3

bash (+ ekran) 160

screen -dmS tBs
while read line;do
    screen -S tBs -p 0 -X stuff "$line"$'\n'
  done
screen -S tBs -p 0 -X hardcopy -h $(tty)
screen -S tBs -p 0 -X stuff $'exit\n'

Gibi bir şey çıktı olacak:

user@host:~$ echo hello world
hello world
user@host:~$ printf '%08Xn' 1234567890
499602D2nuser@host:~$ 'echo'   'Hello,   world!'
Hello,   world!
user@host:~$
user@host:~$ echo heeeeeeelllo | sed 's/(.)1+/1/g'
yes|head -3
heeeeeeelllo
user@host:~$ yes|head -3
echo ''
y
y
y
user@host:~$ echo ''

user@host:~$ echo 'foo bar baz' | sed 's/bar/BAR/' | sed 's/baz/zap/'
foo BAR zap
user@host:~$

Bu benim sistemime bash çağırıyor, buna izin verilmediğini düşünüyorum
tecywiz121

Tabii ki, ama soruyu yeniden okuduktan sonra, bunun herhangi bir kuralı
ihlal

Evet, ancak interresting bir şekilde: Tüm işi yapmak için, çıkmadan önce, tüm geçmişi ilk konsolda dökmekten daha ayrı ve görünmez oturum kullanmak .
F. Hauri

Bu kural kötüye kullanımı konusunda iyiyim. Bence yeterince akıllı - ve soru akıllı kuralların kötüye kullanılmasına izin veriyor. Benden +1.
Konrad Borowski

1

Faktör (208 karakter)

Kurallar, çalışmanın üçüncü bir tarafa ( http://www.compileonline.com/execute_bash_online.php ) boşaltılmasına izin vermediğinden , işte bir çözüm:

USING: arrays http.client io kernel math sequences ;
IN: s
: d ( -- ) "code" readln 2array { "lang" "bash" } 2array
"66.155.39.107/execute_new.php" http-post*
dup length 6 - 86 swap rot subseq write flush d ;

Programı da daha kısa bir tek satır olarak yazabilirsiniz ( 201 karakter):

USING: arrays http.client io kernel math sequences ; [ "code" swap 2array { "lang" "bash" } 2array "66.155.39.107/execute_new.php" http-post* dup length 6 - 86 swap rot subseq write flush ] each-line ;

Kural kötüye kullanımına izin vermemeliydim sanırım. Ah doğru, yaptım. Benden +1 - bunu hiç düşünmezdim.
Konrad Borowski

0

Perl, 135 karakter

#!perl -n
for(/(?:'.*?'|[^|])+/g){s/'//g for@w=/(?:'.*?'|\S)+/g;open($o=(),'-|')or$i&&open(STDIN,'<&',$i),exec@w,exit;$i=$o}print<$o>

Bu kabuk bazı aptalca şeyler yapar. İle etkileşimli bir kabuk başlatın perl shell.plve deneyin:

  • lsstandart çıktı bir terminal olmadığından tek bir sütuna yazdırır. Kabuk, standart çıktıyı bir boruya yönlendirir ve borudan okur.
  • perl -E 'say "hi"; sleep 1' merhaba demek için 1 saniye bekler, çünkü kabuk çıkışı geciktirir.
  • ddbu kabuğa ilk komut olmadıkça 0 bayt okur. Kabuk, standart girdiyi ilk boru hattından sonraki her boru hattı için boş bir borudan yeniden yönlendirir.
  • perl -e '$0 = screamer; print "A" x 1000000' | dd of=/dev/null başarıyla tamamlanır.
  • perl -e '$0 = screamer; print "A" x 1000000' | cat | dd of=/dev/null kabuğu asıyor!
    • Hata # 1: Kabuk, aynı komut satırında üçüncü komutu başlatmadan önce aptalca ilk komutu bekler. Borular dolduğunda, kabuk kilitlenmeye girer. Burada, kabuk çığlık çıkana kadar dd başlamaz, ancak çığlık kedi için bekler ve kedi kabuk için bekler. Çığlığı öldürürseniz (belki pkill -f screamerbaşka bir kabukta), kabuk devam eder.
  • perl -e 'fork and exit; $0 = sleeper; sleep' kabuğu asıyor!
    • Hata # 2: Kabuk, çıkış hattını kapatmak için bir boru hattındaki son komutu bekler. Komut boruyu kapatmadan çıkarsa, kabuk beklemeye devam eder. Uyuyanı öldürürseniz, kabuk devam eder.
  • 'echo $((2+3))'/ bin / sh komutunu çalıştırır. Bu, yalnızca bağımsız değişken özel karakterler içeriyorsa , Perl'ün exec ve sisteminin bir bağımsız değişkeni olan davranışıdır .

Ungolfed sürümü

#!perl -n
# -n wraps script in while(<>) { ... }

use strict;
our($i, $o, @w);

# For each command in a pipeline:
for (/(?:'.*?'|[^|])+/g) {
    # Split command into words @w, then delete quotes.
    s/'//g for @w = /(?:'.*?'|\S)+/g;

    # Fork.  Open pipe $o from child to parent.
    open($o = (), '-|') or
        # Child redirects standard input, runs command.
        $i && open(STDIN, '<&', $i), exec(@w), exit;

    $i = $o;  # Input of next command is output of this one.
}

print <$o>;   # Print output of last command.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.