Linux'taki bash dosya yönlendirmesi, Linux'taki kabuktan (sh) farklıdır?


9

Çalışırken kullanıcıları değiştiren bir komut dosyası yazdım ve standart olarak dosya yönlendirmesini kullanarak yürüttüm. Yani user-switch.sh...

#!/bin/bash

whoami
sudo su -l root
whoami

Ve onunla çalıştırmak bashbana beklediğim davranışı veriyor

$ bash < user-switch.sh
vagrant
root

Bununla birlikte, komut dosyasını çalıştırırsam, shfarklı çıktılar alıyorum

$ sh < user-switch.sh 
vagrant
vagrant

Neden daha bash < user-switch.shfarklı çıktılar veriyor sh < user-switch.sh?

Notlar:

  • Debian Jessie'yi çalıştıran iki farklı kutuda olur

1
Yanıt değil, ancak ilgili sudo su: unix.stackexchange.com/questions/218169/…
Kusalananda

1
/ bin / sh = Debian'da tire var mı? RHEL'de üreme yapamıyorum / bin / sh = bash
Jeff Schaller

1
/bin/shDebian Dash, evet. Şimdiye kadar bunun ne olduğunu bile bilmiyordum. Şimdiye kadar bash ile takıldım.
popedotninja

1
Bash davranış ya herhangi belgelenmiş semantik tarafından işe garanti edilmez.
Charles Duffy

Birisi bugün bana çalıştırdığım senaryonun bash'ın nasıl kullanıldığının anlambilimini ihlal ettiğini belirtti. Bu soruya birkaç harika cevap vardı, ancak muhtemelen bir kullanıcının orta senaryoyu değiştirmemesi gerektiğini belirtmek gerekir. Başlangıçta user-switch.shbir Jenkins işinde çalıştı ve senaryo yürütüldü. Aynı komut dosyasını Jenkins dışında çalıştırmak farklı sonuçlar verdi ve Jenkins'te gördüğüm davranışı elde etmek için dosya yönlendirmeye başvurdum.
popedotninja

Yanıtlar:


12

Benzer bir komut dosyası var, sudoancak benzer sonuçlar yok:

$ cat script.sh
#!/bin/bash
sed -e 's/^/--/'
whoami

$ bash < script.sh
--whoami

$ dash < script.sh
itvirta

İle bash, senaryonun geri kalanı için girdi olarak gider sedile, dashkabuk yorumladığını o.

straceBunlar üzerinde çalışan : dashkomut dosyasının bir bloğunu okur (burada sekiz kB, tüm komut dosyasını tutmak için fazlasıyla yeterli) ve sonra ortaya çıkar sed:

read(0, "#!/bin/bash\nsed -e 's/^/--/'\nwho"..., 8192) = 36
stat("/bin/sed", {st_mode=S_IFREG|0755, st_size=73416, ...}) = 0
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|...

Bu, dosya tanıtıcısının dosyanın sonunda olduğu ve sedherhangi bir girdi görmeyeceği anlamına gelir . Kalan kısım içinde tamponlanır dash. (Komut dosyası 8 kB'lık blok boyutundan daha uzunsa, kalan kısım okunacaktır sed.)

Öte yandan Bash, son komutun sonuna geri döner:

read(0, "#!/bin/bash\nsed -e 's/^/--/'\nwho"..., 36) = 36
stat("/bin/sed", {st_mode=S_IFREG|0755, st_size=73416, ...}) = 0
...
lseek(0, -7, SEEK_CUR)                  = 29
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|...

Giriş bir borudan geliyorsa, burada olduğu gibi:

$ cat script.sh | bash

geri sarma yapılamaz, çünkü borular ve soketler aranmaz. Bu durumda, Bash, aşırı okunmasını önlemek için her seferinde bir karakter okumaya geri döner. ( fd_to_buffered_stream()İçindeinput.c her byte için tam sistem çağrısı yapmak) prensip olarak çok etkili değildir. Uygulamada, okumaların, kabuğun çoğu şeyin tamamen yeni süreçlerin ortaya çıkmasını içerdiği gerçeğine kıyasla büyük bir yük olacağını düşünmüyorum.

Benzer bir durum şudur:

echo -e 'foo\nbar\ndoo' | bash -c 'read a; head -1'

Alt kabuk, readyalnızca ilk satırsonu okuduğundan emin olmak zorundadır , böylece bir headsonraki satırı görür. (Bu da işe dashyarar.)


Başka bir deyişle, Bash, komut dosyasının kendisi ve ondan yürütülen komutlar için aynı kaynağı okumayı desteklemek için ek uzunluklara gider. dashyapmaz. zshVe ksh93Debian içinde paketlenmiş bu konuda Bash ile gidin.


1
Açıkçası, işe yaradığı için biraz şaşırdım.
ilkkachu

harika cevap için teşekkürler! Her iki komutu stracedaha sonra kullanarak çalıştıracağım ve çıktıları karşılaştıracağım. Yine de bu yaklaşımı kullanmamıştım.
popedotninja

2
Evet, soru okumaya tepkim o sürpriz oldu mu bash ile çalışmalarını - Ben kesinlikle iş olmaz şey olarak uzak açtı vardı. Sanırım sadece yarı
sağdım

12

Kabuk, komut dosyasını standart girdiden okuyor. Komut dosyasının içinde, standart girdiyi de okumak isteyen bir komut çalıştırırsınız. Hangi girdi nereye gidecek? Güvenilir bir şekilde söyleyemezsin .

Kabukların çalışma şekli, kaynak kodun bir bölümünü okur, ayrıştırır ve tam bir komut bulmaları durumunda, komutu çalıştırır, daha sonra yığının geri kalanıyla ve dosyanın geri kalanıyla devam eder. Yığın tam bir komut içermiyorsa (sonunda sonlandırıcı bir karakterle - sanırım tüm kabuklar bir satırın sonuna kadar okunur), kabuk başka bir yığın okur vb.

Komut dosyasındaki bir komut, kabuğun komut dosyasını okuduğu aynı dosya tanımlayıcısından okumaya çalışırsa, komut okuduğu son yığından sonra gelenleri bulur. Bu konum tahmin edilemez: kabuğun hangi yığın boyutuna bağlı olduğuna ve yalnızca kabuğa ve sürümüne değil, makine yapılandırmasına, kullanılabilir belleğe vb.

Bash, komutu yürütmeden önce komut dosyasındaki komutun kaynak kodunun sonuna doğru arama yapar. Bu, sadece diğer kabuklar bunu yapmadığı için değil, aynı zamanda kabuk normal bir dosyadan okuyorsa da çalışabileceği için güvenebileceğiniz bir şey değildir. Kabuk bir kanaldan okuyorsa (örneğin ssh remote-host.example.com <local-script-file.sh), okunan veriler okunur ve okunamaz.

Girdiyi komut dosyasındaki bir komuta iletmek istiyorsanız, bunu açıkça, genellikle burada bulunan bir belgeyle yapmanız gerekir . (Burada bir belge genellikle çok satırlı giriş için en uygun olanıdır, ancak herhangi bir yöntem işe yarayacaktır.) Yazdığınız kod, yalnızca komut dosyası normal bir dosyadan kabuğa girdi olarak geçirilirse, yalnızca birkaç kabukta çalışır; Eğer ikincinin whoamigirdi olarak geçmesini sudo …beklediyseniz, betiğin çoğu zaman kabuğun standart girdisine geçirilmediğini aklınızda tutarak tekrar düşünün.

#!/bin/bash
whoami
sudo su -l root <<'EOF'
whoami
EOF

Bu on yılı kullanabilirsiniz sudo -i root. Koşmak sudo sugeçmişten gelen bir hack.

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.