Neden "ps ax" "#!" Olmadan çalışan bir bash betiği bulamıyor? başlık?


13

Bu senaryoyu çalıştırdığımda, öldürülünceye kadar koşmayı amaçladı ...

# foo.sh

while true; do sleep 1; done

... kullanarak bulamıyorum ps ax:

>./foo.sh

// In a separate shell:
>ps ax | grep foo.sh
21110 pts/3    S+     0:00 grep --color=auto foo.sh

... ancak #!betiğe ortak " " başlığını eklersem ...

#! /usr/bin/bash
# foo.sh

while true; do sleep 1; done

... sonra komut dosyası aynı pskomutla sonlandırılabilir ...

>./foo.sh

// In a separate shell:
>ps ax | grep foo.sh
21319 pts/43   S+     0:00 /usr/bin/bash ./foo.sh
21324 pts/3    S+     0:00 grep --color=auto foo.sh

Bu neden böyle?
Bu ilgili bir soru olabilir: " #" sadece bir yorum öneki olduğunu düşündüm ve eğer öyleyse " #! /usr/bin/bash" bir yorumdan başka bir şey değildir. Peki " #!" sadece bir yorumdan daha önemli bir önem taşıyor mu?


Hangi Unix'i kullanıyorsunuz?
Kusalananda

@Kusalananda - Linux linuxbox 3.11.10-301.fc20.x86_64 # 1 SMP Per 5 Aralık 14:01:17 UTC 2013 x86_64 x86_64 x86_64 GNU / Linux
StoneThrow

Yanıtlar:


13

Geçerli etkileşimli kabuk olduğunda bashve #!-line içermeyen bir komut dosyası bashçalıştırdığınızda, komut dosyasını çalıştırır. İşlem ps axçıktıda olduğu gibi görünecektir bash.

$ cat foo.sh
# foo.sh

echo "$BASHPID"
while true; do sleep 1; done

$ ./foo.sh
55411

Başka bir terminalde:

$ ps -p 55411
  PID TT  STAT       TIME COMMAND
55411 p2  SN+     0:00.07 bash

İlişkili:


İlgili bölümler bashkılavuzu oluşturur:

Dosya yürütülebilir biçiminde olmadığından ve dosya bir dizin olmadığından bu yürütme başarısız olursa, bir kabuk komut dosyası , kabuk komutları içeren bir dosya olduğu varsayılır . Bir alt kabuk, yürütmek için ortaya çıkar . Bu alt kabuk kendini yeniden başlatır, böylece etki, ebeveyn tarafından hatırlanan komutların yerleri (aşağıdaki SHELL BUILTIN COMMANDS altındaki karmaya bakın) dışında , komut dosyasını işlemek için yeni bir kabuk çağrılmış gibi olur .

Program ile başlayan bir #!dosyaysa, ilk satırın geri kalanı program için bir yorumlayıcı belirtir. Kabuk, bu yürütülebilir biçimi işlemeyen işletim sistemlerinde belirtilen yorumlayıcıyı yürütür . [...]

Bu ./foo.sh, komut satırında çalıştırmanın , bir satırı foo.sholmadığında #!, dosyadaki komutların alt kabukta çalıştırılmasıyla aynı olduğu anlamına gelir.

$ ( echo "$BASHPID"; while true; do sleep 1; done )

#!Örneğin /bin/bash, uygun bir çizgi ile ,

$ /bin/bash foo.sh

Ben takip düşünüyorum, ama söylediğiniz şey ikinci durumda da doğrudur: bash da ikinci durumda pskomut dosyasını çalıştırır, " /usr/bin/bash ./foo.sh" olarak çalışan komut gösterirken gözlemlenebilir . Yani ilk durumda, dediğiniz gibi, bash betiği çalıştıracaktır, ancak bu betiğin ikinci durumda olduğu gibi çatallı bash yürütülebilir dosyasına "geçirilmesi" gerekmez mi? (ve eğer öyleyse, boru ile grep olabilir ...)
StoneThrow

@StoneThrow Güncellenmiş cevaba bakın.
Kusalananda

"... yeni bir süreç elde etmeniz dışında" - her iki şekilde de yeni bir süreç elde edersiniz, ancak $$alt kabuk durumunda ( echo $BASHPID/ bash -c 'echo $PPID') eskisine işaret eder .
Michael Homer

@MichaelHomer Ah, bunun için teşekkürler! Güncellenecek.
Kusalananda

12

Bir kabuk komut dosyası ile başladığında #!, bu ilk satır kabuk söz konusu olduğunda bir açıklamadır. Ancak ilk iki karakter sistemin başka bir kısmı için anlamlıdır: çekirdek. İki karaktere shebang#! denir . Mesele rolünü anlamak için bir programın nasıl yürütüldüğünü anlamanız gerekir.

Bir programın bir dosyadan yürütülmesi, çekirdeğin eylemini gerektirir. Bu execvesistem çağrısının bir parçası olarak yapılır . Çekirdeğin dosya izinlerini doğrulaması, şu anda çağıran işlemde çalıştırılabilir yürütülebilir dosyayla ilişkili kaynakları (bellek, vb.) Boşaltması, yeni yürütülebilir dosya için kaynak ayırması ve denetimi yeni programa aktarması (ve Bahsetmeyeceğim). execveSistem çağrısı anda çalışan işlemin kodu yerine geçer; forkyeni bir işlem oluşturmak için ayrı bir sistem çağrısı var .

Bunu yapmak için, çekirdek yürütülebilir dosyanın biçimini desteklemelidir. Bu dosya, çekirdeğin anlayacağı şekilde düzenlenmiş makine kodu içermelidir. Kabuk betiği makine kodu içermediğinden, bu şekilde yürütülemez.

Mesele mekanizması, çekirdeğin kodu başka bir programa yorumlama görevini ertelemesini sağlar. Çekirdek yürütülebilir dosyanın başladığını gördüğünde #!, sonraki birkaç karakteri okur ve dosyanın ilk satırını (ön #!ve isteğe bağlı boşluk eksi ) başka bir dosyanın yolu (artı burada tartışmayacağım argümanlar) olarak yorumlar. ). Çekirdeğin dosyayı yürütmesi söylendiğinde ve dosyanın /my/scriptsatırla başladığını gördüğünde #!/some/interpreter, çekirdek /some/interpreterargümanla yürütülür /my/script. Ardından , yürütmesi gereken bir komut dosyası olduğuna /some/interpreterkarar vermek /my/scriptgerekir.

Dosyalardan hiçbiri çekirdeğin anlayabileceği bir formatta yerel kod içermiyorsa ve bir shebang ile başlamıyorsa ne olur? Peki, dosya çalıştırılamaz ve execvesistem çağrısı hata koduyla başarısız olur ENOEXEC(Yürütülebilir format hatası).

Bu hikayenin sonu olabilir, ancak çoğu mermi bir geri dönüş özelliği uygular. Çekirdek geri dönerse ENOEXEC, kabuk dosyanın içeriğine bakar ve bir kabuk betiği gibi olup olmadığını kontrol eder. Kabuk, dosyanın bir kabuk betiği gibi göründüğünü düşünürse, dosyayı tek başına yürütür. Bunun nasıl yapıldığına dair ayrıntılar kabuğa bağlıdır. ps $$Senaryonuza ekleyerek neler olduğunu ve daha fazlasını strace -p1234 -f -eprocess1234'ün kabuğun PID'si olduğu işlemi izleyerek görebilirsiniz .

Bash'da, bu geri dönüş mekanizması çağrılarak uygulanır, forkancak uygulanmaz execve. Çocuk bash işlemi kendi iç durumunu temizler ve çalıştırmak için yeni komut dosyasını açar. Bu nedenle, komut dosyasını çalıştıran işlem hala orijinal bash kodu görüntüsünü ve bash'ı özgün olarak çağırdığınızda iletilen orijinal komut satırı bağımsız değişkenlerini kullanıyor demektir. ATT ksh da aynı şekilde davranır.

% bash --norc
bash-4.3$ ./foo.sh 
  PID TTY      STAT   TIME COMMAND
21913 pts/2    S+     0:00 bash --norc

Aksine Dash, argüman olarak iletilen komut dosyasının yolunu ENOEXECarayarak tepki verir /bin/sh. Başka bir deyişle, kısa çizgiden bir shebangless komut dosyası yürüttüğünüzde, komut dosyasının bir shebang satırı varmış gibi davranır #!/bin/sh. Mksh ve zsh aynı şekilde davranırlar.

% dash
$ ./foo.sh
  PID TTY      STAT   TIME COMMAND
21427 pts/2    S+     0:00 /bin/sh ./foo.sh

Harika, cevabı kavrayın. Bir soru RE: açıkladığınız geri dönüş uygulaması: Bir çocuk bashçatallandığından, argv[]üst öğe ile aynı diziye erişimi olduğunu varsayalım , bu da "orijinal olarak bash'ı çağırdığınızda geçen orijinal komut satırı argümanları" nı bilir ve eğer bu nedenle, çocuk orijinal senaryoyu açık bir argüman olarak geçmemektedir (bu yüzden neden grep tarafından bitirilemez) - bu doğru mu?
StoneThrow

1
Çekirdek sapma davranışını aslında kapatabilirsiniz ( BINFMT_SCRIPTmodül bunu kontrol eder ve genellikle statik olarak çekirdeğe bağlı olmasına rağmen kaldırılabilir / modülerleştirilebilir), ancak belki de gömülü bir sistem dışında neden istediğinizi görmüyorum . Bu olasılık için bir geçici çözüm olarak, telafi etmek için bashbir yapılandırma bayrağı ( HAVE_HASH_BANG_EXEC) vardır!
ErikF

2
@StoneThrow Çocuk bash'ı “orijinal komut satırı argümanlarını bilir”, o yüzden değiştirmez. Bir program ps, komut satırı bağımsız değişkenleri olarak raporları değiştirebilir , ancak yalnızca bir noktaya kadar değiştirebilir: varolan bir bellek arabelleğini değiştirmesi gerekir, bu arabelleği genişletemez. Eğer bash argvkomut dosyasının adını eklemek için değiştirmeye çalışırsa , her zaman işe yaramaz. Çocuk “argümandan geçmedi” çünkü execveçocukta hiçbir zaman bir sistem çağrısı yoktur. Sadece aynı bash işlem görüntüsü çalışıyor.
Gilles 'SO- kötü olmayı bırak'

-1

İlk durumda, komut dosyası geçerli kabuğunuzdan çatallı bir çocuk tarafından çalıştırılır.

Önce çalışmalı echo $$ve daha sonra üst işlem kimliği olarak kabuğunuzun işlem kimliğine sahip bir kabuğa bakmalısınız.

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.