Linux, kabuk kodlarıyla nasıl baş eder?


22

Bu soru için, bir bash kabuk betiği düşünelim, ancak bu sorunun tüm kabuk betiği tipleri için geçerli olması gerekir.

Birisi bir kabuk betiği çalıştırdığında , Linux tüm betiği bir kerede mi yüklüyor (belki belleğe) veya komut dosyası komutlarını tek tek okuyor mu (satır satır)?

Başka bir deyişle, bir kabuk betiğini çalıştırıp yürütme tamamlanmadan önce silersem, yürütme sonlandırılacak mı veya devam ettiği gibi devam edecek mi?


3
Dene. (Devam edecek.)
devnull

1
@ devnull burada gerçekten ilginç bir soru var. Verilmesi, devam edip etmemesi testin önemsiz olup olmadığı, ancak ikili dosyalar (belleğe yüklenen) ve bir shebang satırı içeren komut dosyaları veya shebang satırı olmayan komut dosyaları arasında farklılıklar vardır.
terdon


23
Asıl amacınızın, yürütülmesi sırasında kabuk betiğini silmek için, bir kerede mi yoksa satır satır mı okunacağının önemi yoktur. Unix'te, bir inode gerçekte silinmez (herhangi bir dizinden bağlantı olmasa bile), son açılan dosya kapanana kadar. Başka bir deyişle, kabuğunuz, çalıştırma sırasında satır satırında kabuk komut dosyasını okumasına rağmen, onu silmek hala güvenlidir. Bunun tek istisnası, kabuğunuzun kabuk betiğini her seferinde kapatan ve yeniden açan tür olmasıdır, ancak bunu yaparsa, çok daha büyük (güvenlik) sorunlarınız olur.
Chris Jester-Young

Yanıtlar:


33

Eğer kullanırsanız stracebunu yayınlandığında, bir bir kabuk yürütülür görebilirsiniz.

Örnek

Diyelim ki bu kabuk betiği var.

$ cat hello_ul.bash 
#!/bin/bash

echo "Hello Unix & Linux!"

Kullanarak çalıştırma strace:

$ strace -s 2000 -o strace.log ./hello_ul.bash
Hello Unix & Linux!
$

strace.logDosyanın içine bir göz atmak aşağıdakileri ortaya çıkarır.

...
open("./hello_ul.bash", O_RDONLY)       = 3
ioctl(3, SNDCTL_TMR_TIMEBASE or SNDRV_TIMER_IOCTL_NEXT_DEVICE or TCGETS, 0x7fff0b6e3330) = -1 ENOTTY (Inappropriate ioctl for device)
lseek(3, 0, SEEK_CUR)                   = 0
read(3, "#!/bin/bash\n\necho \"Hello Unix & Linux!\"\n", 80) = 40
lseek(3, 0, SEEK_SET)                   = 0
getrlimit(RLIMIT_NOFILE, {rlim_cur=1024, rlim_max=4*1024}) = 0
fcntl(255, F_GETFD)                     = -1 EBADF (Bad file descriptor)
dup2(3, 255)                            = 255
close(3)     
...

Dosya bir kez okunduğunda, yürütülür:

...
read(255, "#!/bin/bash\n\necho \"Hello Unix & Linux!\"\n", 40) = 40
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 3), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fc0b38ba000
write(1, "Hello Unix & Linux!\n", 20)   = 20
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
read(255, "", 40)                       = 0
exit_group(0)                           = ?

Yukarıda, tüm betiğin tek bir varlık olarak okunduğunu ve daha sonra orada yürütüldüğünü açıkça görebiliyoruz. Böylece en azından Bash'in dosyasında dosyayı okuduğu ve sonra çalıştırdığı "belirir" . Yani sen çalışırken senaryoyu düzenleyebileceğini mi düşünüyorsun?

NOT: Yine de yapma! Çalışan bir komut dosyasıyla neden uğraşmamanız gerektiğini anlamak için okumaya devam edin.

Peki ya diğer tercümanlar?

Ama sorunuz biraz kapalı. Mutlaka dosyanın içeriğini yükleyen Linux değil, içeriği yükleyen tercümandır, bu yüzden tercümanın dosyayı bir seferde tamamen mi yoksa bloklar halinde mi yükleyeceği ile ilgili.

Peki neden dosyayı düzenleyemiyoruz?

Daha büyük bir komut dosyası kullanıyorsanız, ancak yukarıdaki testin biraz yanıltıcı olduğunu fark edeceksiniz. Aslında çoğu tercüman dosyalarını bloklar halinde yerleştirir. Bu, bir dosyanın bloklarını yükledikleri, işledikleri ve daha sonra başka bir blok yükledikleri birçok Unix araçlarında oldukça standart. Bu davranışı, bir süre önce yazdığım U&L Sorular ve Cevapları ile görebilirsiniz grep: “ grep / egrep her seferinde ne kadar metin tüketir? .

Örnek

Aşağıdaki kabuk betiğini yaptığımızı söyleyin.

$ ( 
    echo '#!/bin/bash'; 
    for i in {1..100000}; do printf "%s\n" "echo \"$i\""; done 
  ) > ascript.bash;
$ chmod +x ascript.bash

Bu dosyada sonuçlanan:

$ ll ascript.bash 
-rwxrwxr-x. 1 saml saml 1288907 Mar 23 18:59 ascript.bash

Aşağıdaki içerik türünü içerir:

$ head -3 ascript.bash ; echo "..."; tail -3 ascript.bash 
#!/bin/bash
echo "1"
echo "2"
...
echo "99998"
echo "99999"
echo "100000"

Şimdi bunu yukarıdaki teknikle aynı şekilde çalıştırdığınızda strace:

$ strace -s 2000 -o strace_ascript.log ./ascript.bash
...    
read(255, "#!/bin/bash\necho \"1\"\necho \"2\"\necho \"3\"\necho \"4\"\necho \"5\"\necho \"6\"\necho \"7\"\necho \"8\"\necho \"9\"\necho \"10\"\necho 
...
...
\"181\"\necho \"182\"\necho \"183\"\necho \"184\"\necho \"185\"\necho \"186\"\necho \"187\"\necho \"188\"\necho \"189\"\necho \"190\"\necho \""..., 8192) = 8192

Dosyanın 8KB'lik artışlarla okunduğunu fark edeceksiniz; bu nedenle Bash ve diğer kabukları bir dosyayı tamamen yüklemeyecek, bloklar halinde okuyacaklar.

Referanslar


@ terdon - evet daha önce bu soru-cevap bölümünü gördüğümü hatırlıyorum.
slm

5
40 baytlık bir komut dosyasıyla, elbette, bir satırda okunur. Bir> 8kB betiği ile deneyin.
Gilles 'SO- kötülük olmayı'

Hiç denemedim, ancak tüm işlemler kaldırılan dosyayla ilişkilendirilmiş dosya tanımlayıcısını kapatana kadar dosyaların kaldırılmadığını düşünüyorum, bu nedenle bash kaldırılan dosyadan okumaya devam edebilir.
Farid Nouri Neshat

@Gilles - evet bir örnek ekledim, başlamıştı.
slm

2
Bu davranış sürüme bağlıdır. Bash sürüm 3.2.51 (1) - ile yeniden test ettim ve geçerli satırdan önce tamponlamadığını tespit ettim ( bu stackoverflow yanıtına bakın ).
Gordon Davisson

11

Bu işletim sistemine bağımlı olandan daha fazla kabuk bağımlıdır.

Sürüme bağlı olarak, kshtalep üzerine komut dosyasını 8k veya 64k bayt bloğu ile okuyun.

bashkomut dosyasını satır satır okuyun. Ancak, satırların isteğe bağlı uzunlukta olabileceği gerçeği göz önüne alındığında, bir sonraki satırın başından ayrılmak üzere her seferinde 8176 bayt okur.

Bu basit yapılar için, yani bir düz komutlar takımı içindir.

Kabuk yapılı komutlar kullanılırsa , bir döngü, anahtar, burada bir belge, parantez içine alınmış bir alt kabuk, bir işlev tanımı vb. Ve yukarıdakilerin herhangi bir kombinasyonu gibi ( kabul edilen cevabın cevap vermesi gereken cevap ) Yapımın sonuna kadar önce sözdizimi hatası olmadığından emin olun.for/do/donecase/esac

Aynı kod tekrar tekrar tekrar tekrar okunabildiği için, ancak bu içeriğin normal olarak önbelleğe alındığı gerçeği ile hafifletildiği için bu biraz yetersizdir.

Kabuk tercümanı ne olursa olsun, kabuk betiğin herhangi bir bölümünü tekrar okumakta serbest olduğu için yürütülürken bir kabuk betiğini değiştirmek çok akıllıca olmaz ve bu, eşitleme yapılmadığında beklenmeyen sözdizimi hatalarına neden olabilir.

Aşırı büyük bir komut dosyası yapısını depolayamadığında bash'ın bir bölümleme ihlaliyle çökebileceğini unutmayın ksh93 kusursuz bir şekilde okuyabilir.


7

Bu, betiği çalıştıran tercümanın nasıl çalıştığına bağlıdır. Çekirdeğin yaptığı, çalıştırılacak dosyanın başladığını fark etmek #!, aslında satırın geri kalanını bir program olarak çalıştırır ve çalıştırılabilir argümanı verir. Orada listelenen yorumlayıcı bu dosya satırını satır satır okursa (etkileşimli mermilerin yazdıklarınızla yaptığı gibi), elde ettiğiniz şey budur (ancak çok satırlı döngü yapıları tekrarlanmak üzere okunur ve saklanır); yorumlayıcı dosyayı belleğe sıkıştırırsa, onu işler (belki de Perl ve Pyton gibi ara bir gösterime derler) dosyayı çalıştırmadan önce tam okunur.

Bu arada dosyayı silerseniz, yorumlayıcı kapanana kadar dosya silinmez (her zamanki gibi dosyalar son başvuruda bir dizin girişi ya da açık tutulması gereken bir işlem olduğunda) kaybolur.


4

'X' dosyası:

cat<<'dog' >xyzzy
LANG=C
T=`tty`
( sleep 2 ; ls -l xyzzy >$T ) &
( sleep 4 ; rm -v xyzzy >$T ) &
( sleep 4 ; ls -l xyzzy >$T ) &
echo alive. ; sleep 1
echo alive. ; sleep 1
echo alive. ; sleep 1
echo alive. ; sleep 1
echo alive. ; sleep 1
echo alive. ; sleep 1
echo alive. ; sleep 1
echo alive. ; sleep 1
dog

sh xyzzy

Koşmak:

~/wrk/tmp$ sh x
alive.
alive.
alive.
-rw-r--r-- 1 yeti yeti 287 Mar 23 16:57 xyzzy
alive.
removed `xyzzy'
ls: cannot access xyzzy: No such file or directory
alive.
alive.
alive.
alive.
~/wrk/tmp$ _

IIRC, bir dosya açık kaldığı sürece bir dosya silinmez. Silme işlemi yalnızca verilen DIRENT değerini kaldırır.

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.