“Kaynak file.sh”, “./file.sh”, “sh file.sh”, “kullanarak kabuk komut dosyaları yürütme arasındaki farklar nelerdir? “./File.sh?


13

Koda bir göz atın:

#!/bin/bash
read -p "Eneter 1 for UID and 2 for LOGNAME" choice
if [ $choice -eq 1 ]
then
        read -p "Enter UID:  " uid
        logname=`cat /etc/passwd | grep $uid | cut -f1 -d:`
else
        read -p "Enter Logname:  " logname
fi
not=`ps -au$logname | grep -c bash`
echo  "The number of terminals opened by $logname are $not"

Bu kod, aynı bilgisayarda bir kullanıcı tarafından açılan terminal sayısını bulmak için kullanılır. Şimdi oturum açmış iki kullanıcı var, örneğin x ve y. Şu anda y olarak giriş yaptım ve x kullanıcısında açık 3 terminal var. Bu kod y yukarıda belirtilen farklı yollar kullanarak yürütmek, sonuçları şunlardır:

$ ./file.sh
The number of terminals opened by x are 3

$ bash file.sh
The number of terminals opened by x are 5

$ sh file.sh
The number of terminals opened by x are 3

$ source file.sh
The number of terminals opened by x are 4

$ . ./file.sh
The number of terminals opened by x are 4

Not: Tüm bu yürütülebilir dosyalara 1 ve uid 1000'i geçtim.

Şimdi tüm bunlar arasındaki farkları açıklayabilir misiniz?


fark, hangi kabuğun yürütüldüğü. sh bash değil
j0h

2
Aynı bağlamda yürüttüğünüz için son iki yürütme de farklıdır. Daha burada
Zaka eLab

Diğer kullanıcı (giriş yaptığımız aynı kullanıcı değil) tarafından açılan sayı bash örneklerini (burada terminallerin sayısına eşittir) saymaya çalışıyorum ve neden her durumda farklı sayı geldiğini açıklayabilir misiniz
Ramana Reddy

@RamanaReddy diğer kullanıcı bir komut dosyası çalıştırmış veya yeni bir sekme başlatmış olabilir. Kim bilir?
muru

Yanıtlar:


21

Tek büyük fark bir kaynak oluşturmak ve yürütmek arasındadır. source foo.shkaynak gösterecek ve gösterdiğiniz diğer tüm örnekler yürütülecektir. Daha ayrıntılı olarak:

  1. ./file.sh

    Bu file.sh, geçerli dizinde ( ./) adında bir komut dosyası yürütür . Normalde, çalıştırdığınızda command, kabuk $PATHadlı çalıştırılabilir bir dosya için dizinleri arar command. Eğer tam yol verirsek gibi /usr/bin/commandya ./command, o zaman $PATHgöz ardı edilir ve bu belirli dosya yürütülür.

  2. ../file.sh

    Bu temelde, ./file.shşu anki dizine bakmak yerine file.sh, üst dizine ( ../) bakmak dışındakilerle aynıdır .

  3. sh file.sh

    Bu sh ./file.sh, yukarıdaki gibi file.sh, geçerli dizinde çağrılan komut dosyasını çalıştıracaktır . Aradaki fark, onu shkabukla açıkça çalıştırmanızdır . Ubuntu sistemlerinde, öyle dashve değil bash. Genellikle, komut dosyalarının çalıştırılmaları gereken programı veren bir shebang satırı vardır. Onları farklı biriyle çağırmak bunu geçersiz kılar. Örneğin:

    $ cat foo.sh
    #!/bin/bash  
    ## The above is the shebang line, it points to bash
    ps h -p $$ -o args='' | cut -f1 -d' '  ## This will print the name of the shell
    

    Bu komut dosyası, onu çalıştırmak için kullanılan kabuğun adını yazdırır. Farklı şekillerde çağrıldığında ne döndürdüğünü görelim:

    $ bash foo.sh
    bash
    $ sh foo.sh 
    sh
    $ zsh foo.sh
    zsh
    

    Bu nedenle, bir komut dosyasını çağırmak çağırmak shell script(mevcutsa) shebang satırını geçersiz kılar ve komut dosyasını söylediğiniz kabukla çalıştırır.

  4. source file.sh veya . file.sh

    Buna şaşırtıcı bir şekilde senaryoyu kaynak denir . Anahtar kelime source, kabuk yerleşik .komutunun diğer adıdır . Bu, komut dosyasını geçerli kabukta yürütmenin bir yoludur. Normalde, bir komut dosyası yürütüldüğünde, geçerli kabuktan farklı olan kendi kabuğunda çalıştırılır. Örneklemek gerekirse:

    $ cat foo.sh
    #!/bin/bash
    foo="Script"
    echo "Foo (script) is $foo"
    

    Şimdi, değişkeni fooüst kabukta başka bir şeye ayarlarsam ve komut dosyasını çalıştırırsam, komut dosyası farklı bir değer yazdırır foo(çünkü komut dosyasında da ayarlanır), ancak fooüst kabuktaki değeri değişmez:

    $ foo="Parent"
    $ bash foo.sh 
    Foo (script) is Script  ## This is the value from the script's shell
    $ echo "$foo"          
    Parent                  ## The value in the parent shell is unchanged
    

    Ancak, komut dosyasını yürütmek yerine kaynak yaparsam foo, üst öğenin değeri değiştirilecek şekilde aynı kabukta çalıştırılır :

    $ source ./foo.sh 
    Foo (script) is Script   ## The script's foo
    $ echo "$foo" 
    Script                   ## Because the script was sourced, 
                             ## the value in the parent shell has changed
    

    Bu nedenle, bir betiğin çalıştırdığınız kabuğu etkilemesini istediğiniz birkaç durumda kaynak kullanımı kullanılır. Genellikle kabuk değişkenlerini tanımlamak ve kod bittikten sonra kullanılabilir hale getirmek için kullanılır.


Tüm bunları göz önünde bulundurarak, farklı cevaplar almanızın nedeni, her şeyden önce, komut dosyanızın düşündüğünüzü yapmamasıdır. Çıkışında kaç kez bashgöründüğünü sayar ps. Bu açık terminallerin sayısı değil, çalışan mermilerin sayısıdır (aslında, bu bile değil, ama bu başka bir tartışma). Açıklığa kavuşturmak için betiğinizi biraz basitleştirdim:

#!/bin/bash
logname=terdon
not=`ps -au$logname | grep -c bash`
echo  "The number of shells opened by $logname is $not"

Ve sadece tek bir terminal açıkken çeşitli şekillerde çalıştırın:

  1. Doğrudan başlatma ./foo.sh,.

    $ ./foo.sh
    The number of shells opened by terdon is 1
    

    Burada, mesele hattını kullanıyorsunuz. Bu, komut dosyasının orada ayarlananlar tarafından doğrudan yürütüldüğü anlamına gelir. Bu, betiğin çıktısında gösterilme şeklini etkiler ps. Olarak listelenmek yerine, bash foo.shsadece olarak gösterilir, foo.shbu da greponu özleyeceğiniz anlamına gelir . Aslında çalışan 3 bash örneği vardır: üst işlem, komut dosyasını çalıştıran bash ve pskomutu çalıştıran başka bir örnek . Bu sonuncusu önemlidir, komut değiştirme ( `command`veya $(command)) ile bir komutun başlatılması , üst kabuğun bir kopyasının başlatılmasına ve komutu çalıştırmasına neden olur. Ancak burada, psçıktısını gösterme biçimi nedeniyle bunların hiçbiri gösterilmiyor .

  2. Açık (kabuk) kabukla doğrudan başlatma

    $ bash foo.sh 
    The number of shells opened by terdon is 3
    

    Burada, koştuğunuz bash foo.shiçin çıktısı psgösterilecek bash foo.shve sayılacaktır. Yani, işte, ebeveyn süreç var bashkomut dosyası çalıştıran ve klonlanmış kabuk (çalıştıran ps) artık çünkü tüm gösterilen pskomut kelimesini içerecektir çünkü bunların her gösterecektir bash.

  3. Farklı bir kabukla doğrudan başlatma ( sh)

    $ sh foo.sh
    The number of shells opened by terdon is 1
    

    Birlikte komut dosyası çalıştıran Bunun nedeni farklıdır shve değil bash. Bu nedenle, tek bashörnek komut dosyanızı başlattığınız üst kabuktur. Yukarıda belirtilen diğer tüm mermiler shbunun yerine çalıştırılıyor .

  4. Kaynak bulma (ya .da source, aynı şeyle)

    $ . ./foo.sh 
    The number of shells opened by terdon is 2
    

    Yukarıda açıkladığım gibi, bir komut dosyasının kaynaklanması, üst işlemle aynı kabukta çalışmasına neden olur. Ancak, pskomutu başlatmak için ayrı bir alt kabuk başlatılır ve bu toplamı ikiye getirir.


Son bir not olarak, çalışan süreçleri saymanın doğru yolu ayrıştırmak psdeğil, kullanmaktır pgrep. Sadece koşsaydın bütün bu problemlerden kaçınılmış olurdu

pgrep -cu terdon bash

Bu nedenle, komut dosyanızın her zaman doğru sayıyı yazdıran çalışan bir sürümü (komut yerine koymanın olmadığını not edin):

#!/usr/bin/env bash
user="terdon"

printf "Open shells:"
pgrep -cu "$user" bash

Bu, kaynak oluşturulduğunda 1 ve diğer tüm başlatma yolları için (çünkü komut dosyasını çalıştırmak için yeni bir bash başlatılacak) döndürecektir. Alt shsüreç olmadığından, başlatıldığında hala 1 dönecektir bash.


Komut yerine koymanın üst kabuğun bir kopyasını başlattığını söylediğinizde, bu kopya, komut dosyasını ./foo.sh ile çalıştırdığınızda olduğu gibi bir alt kabuktan nasıl farklıdır?
Didier

Ve komut değiştirme olmadan pgrep çalıştırdığınızda, betiğin çalıştığı kabuğun içinden çalıştığını varsayıyorum? Kaynağa benzer mi?
Didier A.

@didibus Ne demek istediğinden emin değilim. Komut ikamesi bir alt kabukta çalışır; ./foo.shüst öğenin kopyası olmayan yeni bir kabukta çalışır. Örneğin, foo="bar"terminalinizde ayarlayıp yürüten bir komut dosyası çalıştırırsanız echo $foo, komut dosyasının kabuğu değişkenin değerini devralmadığı için boş bir satır alırsınız. pgrepayrı bir ikili dosyadır ve evet çalıştırdığınız komut dosyası tarafından çalıştırılır.
terdon

Temelde açıklığa ihtiyacım var: "komut ikamesi olmadığını not edin". Neden pgrep ikili dosyasını bir komut dosyasından çalıştırmak fazladan bir kabuk eklemiyor, ama ps ikilisini komut değiştirme ile çalıştırmak neden? İkincisi, "ana kabuğun kopyası" ile ilgili açıklamaya ihtiyacım var, ebeveynin kabuk değişkenlerinin çocuğa kopyalandığı bir alt kabuk gibi mi? Komuta değiştirme neden bunu yapar?
Didier
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.