Bir komut dosyasının kaynaklanıp kaynaklanmadığı nasıl belirlenir


217

Kaynaklı exitise aramak istemiyorum bir komut dosyası var .

Eğer kontrol düşündüm $0 == bashama komut dosyası başka bir komut dosyasından kaynaklanırsa veya kullanıcı gibi farklı bir kabuk kaynaklanıyorsa bu sorunları var ksh.

Bir komut dosyasının kaynaklanıp kaynaklanmadığını tespit etmenin güvenilir bir yolu var mı?


2
Bir süre önce benzer bir sorun yaşadım ve her durumda 'çıkıştan' kaçınarak çözdüm; "kill -INT $$" her iki durumda da komut dosyasını güvenli bir şekilde sonlandırır.
JESii

1
Bu cevabı fark ettiniz mi? Kabul edildikten 5 yıl sonra verilir, ancak "piller" dahildir.
raratiru

Yanıtlar:


73

Bu Bash ve Korn arasında taşınabilir gibi görünüyor:

[[ $_ != $0 ]] && echo "Script is being sourced" || echo "Script is a subshell"

Buna benzer bir satır ya da pathname="$_"(daha sonraki bir test ve eylemle) gibi bir ödevin , komut dosyasının ilk satırında veya shebang'dan sonraki satırda olması gerekir (eğer kullanılıyorsa, altında çalışabilmesi için ksh için olmalıdır) çoğu koşul).


10
Maalesef çalışacağı garanti edilmez. Kullanıcı ayarladıysa BASH_ENV, $_komut dosyasının üst kısmında çalıştırılan son komut olacaktır BASH_ENV.
Mikel

30
Komut dosyasını yürütmek için bash kullanırsanız da çalışmaz, örneğin $ bash script.sh, komut dosyası çağrıldığında beklediğiniz durumda ./script.sh yerine $ _ / bin / bash olur. bu şekilde: $ ./script.sh Her durumda ile tespit etmek $_bir problemdir.
Wirawan Purwanto

2
Bu invokasyon yöntemlerini kontrol etmek için ek testler dahil edilebilir.
sonraki duyuruya kadar duraklatıldı.

8
Ne yazık ki, bu yanlış! bkz Cevabımı
F. Hauri

8
Özetlemek gerekirse: Bu yaklaşım tipik olarak etkili olmakla birlikte, sağlam değildir ; aşağıdaki 2 senaryoda başarısız olur: (a) bash script(bu çözümün kaynaklandığı gibi yanlış bildirdiği kabuk yürütülebilir yoluyla çağırma ) ve (b) (çok daha az olası) echo bash; . script( $_komut dosyasını kaynaklayan kabukla eşleşirse, bu çözüm bir alt kabuk ). Yalnızca kabuğa özgü özel değişkenler (örn. $BASH_SOURCE) Sağlam çözümlere izin verir (bunun sonucunda POSIX uyumlu sağlam bir çözüm yoktur). Bu ise sağlam bir çapraz kabuk testi zanaat, hantal olsa mümkündür.
mklement0

170

Bash sürümünüz BASH_SOURCE dizi değişkenini biliyorsa, aşağıdakini deneyin:

# man bash | less -p BASH_SOURCE
#[[ ${BASH_VERSINFO[0]} -le 2 ]] && echo 'No BASH_SOURCE array variable' && exit 1

[[ "${BASH_SOURCE[0]}" != "${0}" ]] && echo "script ${BASH_SOURCE[0]} is being sourced ..."

11
$ BASH_SOURCE tam olarak bu amaç için tasarlandığından belki de en temiz yol budur.
con-f-use

4
Bunun OP'nin belirttiği bir koşul olan ksh altında çalışmayacağını unutmayın.
sonraki duyuruya kadar duraklatıldı.

2
${BASH_SOURCE[0]}Sadece kullanmak için bir neden var mı $BASH_SOURCE? Ve ${0}vs $0?
hraban

4
BASH_SOURCEen sonuncusu olan kaynakların yığın izini tutan bir dizi değişkenidir ( kılavuza bakınız ) ${BASH_SOURCE[0]}. Parantezler burada bash'a değişken adının bir parçası olduğunu söylemek için kullanılır. $0Bu durumda gerekli değildirler , ama onlar da incinmezler. ;)
Konrad

4
@Konrad ve genişletirseniz $array, ${array[0]}varsayılan olarak alırsınız . Yani, yine, bir sebep var mı [...]?
Charles Duffy

133

İçin sağlam çözeltiler bash, ksh,zsh , bir de dahil olmak üzere , çapraz kabuk bir artı bir makul sağlam POSIX uyumlu çözeltisi :

  • Verilen sürüm numaraları, işlevselliğin doğrulandığı modellerdir - büyük olasılıkla, bu çözümler daha önceki sürümlerde de çalışır - geri bildirim hoş geldiniz .

  • Kullanılması POSIX yalnızca özellikleri (örneğin olduğu gibi dashgibi görev yapan /bin/sh, orada Ubuntu üzerine) hiçbir sağlam yolu iyi için aşağıya bakın - Bir senaryo kaynaklı olup olmadığını belirlemek için yakınlaştırılması .

Tek gömlek takip eder - aşağıdaki açıklama; çapraz kabuk sürümü karmaşıktır, ancak sağlam bir şekilde çalışması gerekir:

  • bash (3.57 ve 4.4.19'da doğrulandı)

    (return 0 2>/dev/null) && sourced=1 || sourced=0
  • ksh (93u + 'da doğrulandı)

    [[ $(cd "$(dirname -- "$0")" && 
       printf '%s' "${PWD%/}/")$(basename -- "$0") != "${.sh.file}" ]] &&
         sourced=1 || sourced=0
  • zsh ( 5.0.5'te doğrulandı) - bunu bir fonksiyonun dışında aradığınızdan emin olun

    [[ $ZSH_EVAL_CONTEXT =~ :file$ ]] && sourced=1 || sourced=0
  • çapraz kabuk (bash, ksh, zsh)

    ([[ -n $ZSH_EVAL_CONTEXT && $ZSH_EVAL_CONTEXT =~ :file$ ]] || 
     [[ -n $KSH_VERSION && $(cd "$(dirname -- "$0")" &&
        printf '%s' "${PWD%/}/")$(basename -- "$0") != "${.sh.file}" ]] || 
     [[ -n $BASH_VERSION ]] && (return 0 2>/dev/null)) && sourced=1 || sourced=0
  • POSIX uyumlu ; bir liner teknik nedenlerden ötürü (tek boru hattı) değil tamamen sağlam (alt bakınız):

    sourced=0
    if [ -n "$ZSH_EVAL_CONTEXT" ]; then 
      case $ZSH_EVAL_CONTEXT in *:file) sourced=1;; esac
    elif [ -n "$KSH_VERSION" ]; then
      [ "$(cd $(dirname -- $0) && pwd -P)/$(basename -- $0)" != "$(cd $(dirname -- ${.sh.file}) && pwd -P)/$(basename -- ${.sh.file})" ] && sourced=1
    elif [ -n "$BASH_VERSION" ]; then
      (return 0 2>/dev/null) && sourced=1 
    else # All other shells: examine $0 for known shell binary filenames
      # Detects `sh` and `dash`; add additional shell filenames as needed.
      case ${0##*/} in sh|dash) sourced=1;; esac
    fi

Açıklama:


darbe

(return 0 2>/dev/null) && sourced=1 || sourced=0

Not: Teknik, orijinal çözümden daha sağlam olduğu için user5754163'ün yanıtından uyarlanmıştır , [[ $0 != "$BASH_SOURCE" ]] && sourced=1 || sourced=0[1]

  • Bash returnyalnızca işlevlerden ve bir komut dosyasının en üst düzey kapsamındaki ifadelere yalnızca komut dosyası kaynaklanıyorsa izin verir .

    • Eğer returnbir üst düzey kapsamında kullanılan olmayan kaynaklı komut, bir hata mesajı çıkar, ve çıkış kodu olarak ayarlanır 1.
  • (return 0 2>/dev/null)yürütür returnbir de alt kabuğa ve bastırır hata mesajı; daha sonra çıkış kodu , değişkenin buna göre ayarlanması için ve işleçleriyle birlikte kullanılan komut dosyasının kaynaklanıp kaynaklanmadığını ( 0) belirtir .1&&||sourced

    • Kaynaklı returnbir komut dosyasının üst düzey kapsamında yürütülmesi komut dosyasından çıkacağı için alt kabuk kullanılması gereklidir .
    • Şapkayı , operand 0olarak açıkça kullanarak komutu daha sağlam hale getiren @Haozhun'a ipucu return; not: bash yardımı başına return [N]: "N atlanırsa, dönüş durumu son komutun durumudur." Sonuç olarak, returnkullanıcının kabuğundaki son komut sıfırdan farklı bir dönüş değerine sahipse , önceki sürüm [yalnızca bir işlenen olmadan kullanılan ] yanlış sonuç üretir.

ksh

[[ \
   $(cd "$(dirname -- "$0")" && printf '%s' "${PWD%/}/")$(basename -- "$0") != \
   "${.sh.file}" \
]] && 
sourced=1 || sourced=0

Özel değişken ${.sh.file}bir şekilde benzerdir $BASH_SOURCE; bash, zsh ve dash'de ${.sh.file}bir sözdizimi hatasına neden olduğunu unutmayın , bu nedenle bunu çok kabuklu komut dosyalarında koşullu olarak yürüttüğünüzden emin olun .

Bash'ın aksine $0ve kaynaklanmamış durumda tam olarak aynı ${.sh.file}olduğu garanti EDİLMEZ , göreceli bir yol olabileceği gibi , her zaman tam bir yoldur, bu yüzden karşılaştırmadan önce tam bir yola çözümlenmelidir.$0${.sh.file}$0


zsh

[[ $ZSH_EVAL_CONTEXT =~ :file$ ]] && sourced=1 || sourced=0

$ZSH_EVAL_CONTEXTdeğerlendirme bağlamı hakkında bilgi içerir - bunu bir işlevin dışında olarak adlandırın. Kaynaklı bir komut dosyasının içinde [en üst düzey kapsamı] ile $ZSH_EVAL_CONTEXT biter:file .

Uyarı: Komut ikamesinde, zsh eklenir :cmdsubst, bu yüzden orada test $ZSH_EVAL_CONTEXTedin :file:cmdsubst$.


Yalnızca POSIX özelliklerini kullanma

Eğer belirli varsayımlar yapmak için istekli iseniz, olabilir bir hale makul ama tahmin geçirmez kandırmayalım senaryonuz dayalı kaynaklı ediliyor olmadığı konusunda ikili dosya bilerek Komut dosyanızın olabilir kabuklarından .
Özellikle, komut dosyanız başka bir komut dosyası tarafından kaynaklanıyorsa, bu yaklaşımın başarısız olduğu anlamına gelir .

İçinde "kaynaklı çağırmaları nasıl baş edilir" bölümü bu cevap benim sınır durumları tartışır olamaz sadece ayrıntılı olarak özellikleri POSIX ile ele alınması.

Bu standart davranış dayanır $0, zshörneğin vermez için, değil sergilerler.

Bu nedenle, en güvenli yaklaşım, yukarıdaki sağlam, kabuğa özgü yöntemleri kalan tüm kabuklar için bir geri dönüş çözümü ile birleştirmektir .

Stéphane Desneux'a şapka ucu ve ilham için cevabı (çapraz kabuk ifadesi ifademish uyumlu bir ifadeye dönüştürmek ifve diğer kabuklar için bir işleyici eklemek).

sourced=0
if [ -n "$ZSH_EVAL_CONTEXT" ]; then 
  case $ZSH_EVAL_CONTEXT in *:file) sourced=1;; esac
elif [ -n "$KSH_VERSION" ]; then
  [ "$(cd $(dirname -- $0) && pwd -P)/$(basename -- $0)" != "$(cd $(dirname -- ${.sh.file}) && pwd -P)/$(basename -- ${.sh.file})" ] && sourced=1
elif [ -n "$BASH_VERSION" ]; then
  (return 0 2>/dev/null) && sourced=1 
else # All other shells: examine $0 for known shell binary filenames
  # Detects `sh` and `dash`; add additional shell filenames as needed.
  case ${0##*/} in sh|dash) sourced=1;; esac
fi

[1] user1902689 keşfetti [[ $0 != "$BASH_SOURCE" ]]bir senaryoyu yürütmek zaman verimlerle yanlış Pozitif bulunan$PATH kendi ileterek sadece dosya için bashikili; örneğin bash my-script, çünkü $0tam o sırada ise my-script, oysa $BASH_SOURCEolduğunu tam yol . Normalde komut dosyaları çağırmak için bu tekniği kullanmak ister iken $PATH- sadece onları çağırmak istiyorum doğrudan ( my-script) - bu olduğunu birleştirildiğinde yararlı -xiçin hata ayıklama .


1
böyle kapsamlı bir cevap için kudos.
DrUseful

75

@ DennisWilliamson'ın cevabını okuduktan sonra bazı sorunlar var, aşağıya bakın:

Bu soru şu anlama gelir: ve , bu cevapta başka bir bölüm daha var. ... aşağıya bakınız.

Basit yol

[ "$0" = "$BASH_SOURCE" ]

Deneyelim (anında) çünkü bu bash ;-):

source <(echo $'#!/bin/bash
           [ "$0" = "$BASH_SOURCE" ] && v=own || v=sourced;
           echo "process $$ is $v ($0, $BASH_SOURCE)" ')
process 29301 is sourced (bash, /dev/fd/63)

bash <(echo $'#!/bin/bash
           [ "$0" = "$BASH_SOURCE" ] && v=own || v=sourced;
           echo "process $$ is $v ($0, $BASH_SOURCE)" ')
process 16229 is own (/dev/fd/63, /dev/fd/63)

Bunun sourceyerine kapalı .okunabilirlik için kullanın ( .bir takma ad olduğu gibi source):

. <(echo $'#!/bin/bash
           [ "$0" = "$BASH_SOURCE" ] && v=own || v=sourced;
           echo "process $$ is $v ($0, $BASH_SOURCE)" ')
process 29301 is sourced (bash, /dev/fd/63)

Süreç kalmak sırasında işlem sayısı değiştirmemesi Not kaynaklı :

echo $$
29301

Neden kullanmamaya $_ == $0karşılaştırma

Birçok vaka sağlamak için, gerçek bir komut dosyası yazmaya başlar :

#!/bin/bash

# As $_ could be used only once, uncomment one of two following lines

#printf '_="%s", 0="%s" and BASH_SOURCE="%s"\n' "$_" "$0" "$BASH_SOURCE"
[[ "$_" != "$0" ]] && DW_PURPOSE=sourced || DW_PURPOSE=subshell

[ "$0" = "$BASH_SOURCE" ] && BASH_KIND_ENV=own || BASH_KIND_ENV=sourced;
echo "proc: $$[ppid:$PPID] is $BASH_KIND_ENV (DW purpose: $DW_PURPOSE)"

Bunu şu adlı bir dosyaya kopyalayın testscript:

cat >testscript   
chmod +x testscript

Şimdi test edebiliriz:

./testscript 
proc: 25758[ppid:24890] is own (DW purpose: subshell)

Bu iyi.

. ./testscript 
proc: 24890[ppid:24885] is sourced (DW purpose: sourced)

source ./testscript 
proc: 24890[ppid:24885] is sourced (DW purpose: sourced)

Bu iyi.

Ancak, -xbayrak eklemeden önce bir komut dosyasını test etmek için :

bash ./testscript 
proc: 25776[ppid:24890] is own (DW purpose: sourced)

Veya önceden tanımlanmış değişkenleri kullanmak için:

env PATH=/tmp/bintemp:$PATH ./testscript 
proc: 25948[ppid:24890] is own (DW purpose: sourced)

env SOMETHING=PREDEFINED ./testscript 
proc: 25972[ppid:24890] is own (DW purpose: sourced)

Bu artık işe yaramayacak.

Yorumu 5. satırdan 6. satıra taşımak daha okunabilir bir cevap verecektir:

./testscript 
_="./testscript", 0="./testscript" and BASH_SOURCE="./testscript"
proc: 26256[ppid:24890] is own

. testscript 
_="_filedir", 0="bash" and BASH_SOURCE="testscript"
proc: 24890[ppid:24885] is sourced

source testscript 
_="_filedir", 0="bash" and BASH_SOURCE="testscript"
proc: 24890[ppid:24885] is sourced

bash testscript 
_="/bin/bash", 0="testscript" and BASH_SOURCE="testscript"
proc: 26317[ppid:24890] is own

env FILE=/dev/null ./testscript 
_="/usr/bin/env", 0="./testscript" and BASH_SOURCE="./testscript"
proc: 26336[ppid:24890] is own

Daha güçlü: Şimdi ...

Kullanmadığım için bir sürü adam sayfasında okuduktan sonra denemelerim var:

#!/bin/ksh

set >/tmp/ksh-$$.log

Bunu şunlara kopyala testfile.ksh:

cat >testfile.ksh
chmod +x testfile.ksh

İki kez çalıştırın:

./testfile.ksh
. ./testfile.ksh

ls -l /tmp/ksh-*.log
-rw-r--r-- 1 user user   2183 avr 11 13:48 /tmp/ksh-9725.log
-rw-r--r-- 1 user user   2140 avr 11 13:48 /tmp/ksh-9781.log

echo $$
9725

ve bakın:

diff /tmp/ksh-{9725,9781}.log | grep ^\> # OWN SUBSHELL:
> HISTCMD=0
> PPID=9725
> RANDOM=1626
> SECONDS=0.001
>   lineno=0
> SHLVL=3

diff /tmp/ksh-{9725,9781}.log | grep ^\< # SOURCED:
< COLUMNS=152
< HISTCMD=117
< LINES=47
< PPID=9163
< PS1='$ '
< RANDOM=29667
< SECONDS=23.652
<   level=1
<   lineno=1
< SHLVL=2

Bazı içinde herited değişken vardır kaynaklı gerçekten ilgili vadede, ama hiçbir şey ...

Buna $SECONDSyakın olduğunu bile kontrol edebilirsiniz 0.000, ancak bu sadece manuel kaynaklı vakaları garanti eder ...

Hatta ebeveynin ne olduğunu kontrol etmeyi deneyebilirsiniz :

Bunu içine yerleştirin testfile.ksh:

ps $PPID

daha:

./testfile.ksh
  PID TTY      STAT   TIME COMMAND
32320 pts/4    Ss     0:00 -ksh

. ./testfile.ksh
  PID TTY      STAT   TIME COMMAND
32319 ?        S      0:00 sshd: user@pts/4

ya da ps ho cmd $PPID, ama bu sadece bir seviye alt iş için çalışır ...

Üzgünüm, bunu yapmanın güvenilir bir yolunu bulamadım .


[ "$0" = "$BASH_SOURCE" ] || [ -z "$BASH_SOURCE" ]pipe ( cat script | bash) ile okunan kodlar için .
hakre

2
Bunun .bir takma ad olmadığını unutmayın, sourceaslında tam tersi. source somescript.shbir Bash-ism ve taşınabilir değil . somescript.sh, POSIX ve taşınabilir IIRC.
dragon788

32

BASH_SOURCE[]Cevap (daha sonra bash-3.0 ve üzeri) olsa da, en basit görünüyor BASH_SOURCE[]edilir bir işlev vücut dışında çalışmalarına belgelenmiş değil (şu anda adam sayfası ile anlaşmazlık içinde, iş olur).

Wirawan Purwanto tarafından önerildiği gibi en sağlam yol, FUNCNAME[1] bir işlev içinde kontrol etmektir :

function mycheck() { declare -p FUNCNAME; }
mycheck

Sonra:

$ bash sourcetest.sh
declare -a FUNCNAME='([0]="mycheck" [1]="main")'
$ . sourcetest.sh
declare -a FUNCNAME='([0]="mycheck" [1]="source")'

Bu çıktısını kontrol eşdeğerdir callerdeğerler, mainve sourcearayanın bağlamı ayırt eder. Kullanarak çıktıyı FUNCNAME[]yakalayıp ayrıştırabilirsiniz caller. Doğru olmak için yerel arama derinliğinizi bilmeniz veya hesaplamanız gerekir. Bir komut dosyasının başka bir işlev veya komut dosyasından kaynaklandığı durumlar dizinin (yığının) daha derin olmasına neden olur. ( FUNCNAMEözel bir bash dizi değişkendir, çağrı yığınına karşılık gelen bitişik dizinler olmamalıdır unset.

function issourced() {
    [[ ${FUNCNAME[@]: -1} == "source" ]]
}

(Bash-4.2 ve sonrasında ${FUNCNAME[-1]}dizideki son öğe için daha basit formu kullanabilirsiniz . Aşağıdaki Dennis Williamson'ın yorumu sayesinde iyileştirilmiş ve basitleştirilmiştir.)

Ancak, belirtildiği gibi sorun " Ben kaynak " çıkış "olarak adlandırmak istemiyorum bir komut dosyası var ". bashBu durum için ortak deyim:

return 2>/dev/null || exit

Komut dosyası kaynaklanıyorsa return, kaynak kodunu sonlandırır ve arayana geri döner.

Komut dosyası yürütülüyorsa, returnbir hata döndürür (yeniden yönlendirilir) ve exitkomut dosyasını normal şekilde sonlandırır. Her ikisi de returnve exitgerekirse bir çıkış kodu alabilir.

Ne yazık ki, bu işe yaramaz ksh(en azından burada sahip olduğum AT&T türevli versiyonda değil), bir fonksiyonun veya nokta kaynaklı komut dosyasının dışında çağrıldığında returneşdeğer olarak davranır exit.

Güncel : Ne olabilir çağdaş sürümlerinde yapmak kshözel değişkeni kontrol etmektir .sh.levelişlev çağrısı derinliğine ayarlanır. Çağrılan bir komut dosyası için bu başlangıçta ayarlanmayacak, nokta kaynaklı bir komut dosyası için 1 olarak ayarlanacaktır.

function issourced {
    [[ ${.sh.level} -eq 2 ]]
}

issourced && echo this script is sourced

Bu, bash sürümü kadar sağlam değildir issourced(), test ettiğiniz dosyaya en üst düzeyde veya bilinen bir işlev derinliğinde çağırmanız gerekir .

(Ayrıca bash dizisini taklit etmek için bir disiplin işlevi ve bazı hata ayıklama tuzağı hile kullanan github üzerinde bu kod ilginizi çekebilir .)kshFUNCNAME

Burada kanonik cevap: http://mywiki.wooledge.org/BashFAQ/109 da $-kabuk durumunun başka bir göstergesi (kusurlu olsa da) olarak sunuyor .


Notlar:

  • "main" ve "source" ( yerleşikFUNCNAME[] öğeyi geçersiz kılan ) adlı bash işlevleri oluşturmak mümkündür , bu adlar görünebilir, ancak yalnızca bu dizideki son öğe sınandığı sürece bir belirsizlik yoktur.
  • Benim için iyi bir cevabım yok pdksh. Bulduğum en yakın şey pdksh, yalnızca bir komut dosyasının her kaynağının yeni bir dosya tanımlayıcı açtığı (orijinal komut dosyası için 10 ile başlayan) için geçerlidir. Neredeyse kesinlikle güvenmek istediğiniz bir şey değil ...

${FUNCNAME[(( ${#FUNCNAME[@]} - 1 ))]}Yığındaki son (alt) öğeyi almaya ne dersiniz ? Sonra "ana" (OP için olumsuz) karşı test benim için en güvenilir oldu.
Adrian Günter

Bir kümem varsa, çalıştırdığımda dizinin PROMPT_COMMANDson dizini olarak FUNCNAMEgörünür source sourcetest.sh. Çek (arayan evrilmesi mainson indeksi gibi) daha sağlam görünüyor: is_main() { [[ ${FUNCNAME[@]: -1} == "main" ]]; }.
dimo414

1
Man sayfası FUNCNAME, yalnızca işlevlerde kullanılabilen durumları belirtir . İle benim testlere göre declare -p FUNCNAME, bashfarklı davranır. v4.3 fonksiyonların dışında bir hata verirken v4.4 verir declare -a FUNCNAME. Hem (!) Dönüş mainiçin ${FUNCNAME[0]}(o yürütüldüğünde ise) ana senaryoda ise $FUNCNAMEhiçbir şey verir. Ve: $BASH_SOURCEDışarıda işlevler kullanarak "ab" pek çok komut dosyası var , bunun değiştirilebileceğinden veya değiştirileceğinden şüpheliyim.
Tino

24

Editörün notu: Bu yanıtın çözümü sağlam bir şekilde çalışıyor, ancak bashsadece. Aerodinamik hale getirilebilir
(return 2>/dev/null).

TL; DR

Bir returnifade çalıştırmayı deneyin . Komut dosyası kaynaklanmazsa, bu bir hataya neden olur. Bu hatayı yakalayabilir ve istediğiniz gibi devam edebilirsiniz.

Bunu bir dosyaya koyun ve diyelim, test.sh:

#!/usr/bin/env sh

# Try to execute a `return` statement,
# but do it in a sub-shell and catch the results.
# If this script isn't sourced, that will raise an error.
$(return >/dev/null 2>&1)

# What exit code did that give?
if [ "$?" -eq "0" ]
then
    echo "This script is sourced."
else
    echo "This script is not sourced."
fi

Doğrudan yürütün:

shell-prompt> sh test.sh
output: This script is not sourced.

Kaynak:

shell-prompt> source test.sh
output: This script is sourced.

Benim için, bu zsh ve bash ile çalışır.

açıklama

returnEğer bir fonksiyonun dışında veya komut dosyası kaynaklı değilse bunu yürütmeye çalışırsanız ifade bir hata yükseltecektir. Bunu bir kabuk isteminden deneyin:

shell-prompt> return
output: ...can only `return` from a function or sourced script

Bu hata mesajını görmenize gerek yoktur, böylece çıktıyı dev / null değerine yönlendirebilirsiniz:

shell-prompt> return >/dev/null 2>&1

Şimdi çıkış kodunu kontrol edin. 0 Tamam anlamına gelir (hata oluşmaz), 1 hata oluştuğunu gösterir:

shell-prompt> echo $?
output: 1

Ayrıca, returnifadeyi bir alt kabuğun içinde yürütmek de istersiniz . Ne zaman returndeyimi çalıştırır. . . iyi . . . İadeler. Alt kabukta yürütürseniz, komut dosyanızdan geri dönmek yerine o alt kabuktan geri döner. Alt kabukta yürütmek için içine sarın $(...):

shell-prompt> $(return >/dev/null 2>$1)

Şimdi, alt kabuğun çıkış kodunu görebilirsiniz, bu 1 olmalıdır, çünkü alt kabuğun içinde bir hata oluştu:

shell-prompt> echo $?
output: 1

Bu benim için başarısız 0.5.8-2.1ubuntu2 $ readlink $(which sh) dash $ . test.sh This script is sourced. $ ./test.sh This script is sourced.
Phil Rutschman

3
POSIX returnen üst düzeyde ne yapılması gerektiğini belirtmez ( pubs.opengroup.org/onlinepubs/9699919799/utilities/… ). dashKabuk davranır returngibi üst düzeyde exit. Bunun gibi bir tekniğin özelliği olan en üst düzeyde gibi bashveya zshizin vermeyen diğer mermiler return.
user5754163

Alt $kabuktan önce çıkarırsanız sh olarak çalışır . Yani, - (return >/dev/null 2>&1)yerine kullanın $(return >/dev/null 2>&1)ama sonra bash'da çalışmayı durdurur.
İsimsiz

@Eponymous: dashBu çözümün çalışmadığı shdurumlarda, örneğin Ubuntu'da olduğu gibi , bu çözüm genellikle çalışmaz sh. Çözüm, Bash 3.2.57 ve 4.4.5'te benim için işe yarıyor - daha $önce veya olmadan (...)(bunun için asla iyi bir neden yok $).
mklement0

2
returnbir exlcit dönüş değeri olmadan sourcekomutlar kötü çıkış komutundan hemen sonra verilirken kesilir . Geliştirme düzenlemesi önerildi.
DimG

12

FWIW, diğer tüm cevapları okuduktan sonra benim için aşağıdaki çözümü buldum:

Güncelleme: Aslında, birisi benimkini de etkileyen başka bir cevapta düzeltilmiş bir hatayı fark etti . Buradaki güncellemenin de bir gelişme olduğunu düşünüyorum (merak ediyorsanız düzenlemelere bakın).

Bu , işlevin dışında tutulan bazı bilgileri (ayarlar gibi) öğrenmek için farklı kabuklarla başlayan#!/bin/bash ancak farklı kabuklardan kaynaklanabilecek tüm komut dosyaları için mainçalışır.

Aşağıdaki yorumlara göre, bu cevap görünüşte tüm bashvaryantlar için işe yaramıyor . Ayrıca /bin/sh, dayalı sistemler için değil bash. IE bashMacOS üzerinde v3.x için başarısız . (Şu anda bunu nasıl çözeceğimi bilmiyorum.)

#!/bin/bash

# Function definitions (API) and shell variables (constants) go here
# (This is what might be interesting for other shells, too.)

# this main() function is only meant to be meaningful for bash
main()
{
# The script's execution part goes here
}

BASH_SOURCE=".$0" # cannot be changed in bash
test ".$0" != ".$BASH_SOURCE" || main "$@"

Son 2 satır yerine, BASH_SOURCEdiğer kabuklarda ayarlanmamak ve set -eçalışmasına izin vermek için aşağıdaki (bence daha az okunabilir) kodu kullanabilirsiniz main:

if ( BASH_SOURCE=".$0" && exec test ".$0" != ".$BASH_SOURCE" ); then :; else main "$@"; fi

Bu komut dosyası tarifi aşağıdaki özelliklere sahiptir:

  • bashNormal yoldan yürütülürse mainçağrılır. Lütfen bunun bash -x script( scriptbir yol içermeyen) gibi bir çağrı içermediğini unutmayın , aşağıya bakın.

  • Tarafından kaynaklı ise bash, mainçağıran komut aynı ada sahip olursa, sadece, denir. (Kendisini kaynakları veya üzeri Örneğin, bash -c 'someotherscript "$@"' main-script args..nerede main-scriptolmalı ne testolarak gördüğü $BASH_SOURCE).

  • Başka evalbir kabuk tarafından kaynaklanırsa / yürütülürse / okunursa / çağrılırsa bash, mainçağrılmaz ( BASH_SOURCEher zaman farklıdır $0).

  • mainboş dize basholarak ayarlanmadıkça, stdin'den komut dosyasını okuyorsa çağrılmaz $0:( exec -a '' /bin/bash ) <script

  • Tarafından değerlendirilir Eğer bashbirlikte eval ( eval "`cat script`" tüm tırnak! Önemlidir , bu çağrılar başka komut dosyası içinde) main. Eğer evaldoğrudan komut ile çalıştırılır, bu yazı stdin'i okunur önceki durumda, benzer. ( BASH_SOURCEboştur, ancak $0genellikle /bin/bashtamamen farklı bir şeye zorlanmazsa).

  • Eğer mainçağrılmaz, bu dönüşü yapar true( $?=0).

  • Bu beklenmedik davranışa dayanmaz (daha önce belgelenmemiş yazdım, ancak unsetdeğiştiremediğiniz veya değiştiremediğiniz hiçbir belge bulamadım BASH_SOURCE):

    • BASH_SOURCEbash ayrılmış bir dizidir . Ancak BASH_SOURCE=".$0"değiştirmeye izin vermek çok tehlikeli bir solucan kutusu açacaktır, bu yüzden beklentim, bunun bir etkisi olmaması gerekir (belki de, gelecekteki bazı versiyonlarında çirkin bir uyarı ortaya çıkarsa)bash ).
    • BASH_SOURCEİşlevlerin dışında çalışan hiçbir belge yoktur . Ancak bunun tersi (yalnızca işlevlerde çalıştığı) belgelenmemiştir. Gözlem, çalıştığı ( bashv4.3 ve v4.4 ile test edildi , maalesef bashartık v3.x'im yok) ve $BASH_SOURCEgözlemlendiği gibi çalışmayı durdurursa , çok fazla komut dosyasının kırılacağı . Bu yüzden benim beklentim, bu BASH_SOURCEgelecekteki sürümleri için olduğu gibi kalırbash de geçerli.
    • Aksine (güzel bulmak, BTW!) Düşünün ( return 0 ), hangi 0kaynaklı ve 1değilse kaynak verir . Bu sadece benim için biraz beklenmedik geliyor ve (oradaki okumalara göre) POSIX, returnalt kabuktan tanımlanmamış davranış olduğunu söylüyor (ve returnburada açıkça bir alt kabuktan). Belki de bu özellik sonunda değiştirilemeyecek kadar yaygın bir şekilde kullanılır, ancak AFAICS, gelecekteki bazı bashsürümlerin yanlışlıkla bu durumda dönüş davranışını değiştirme şansı çok daha yüksektir .
  • Ne yazık ki bash -x script 1 2 3çalışmaz main. ( Yolu olmayan script 1 2 3yerleri karşılaştırın script). Geçici çözüm olarak aşağıdakiler kullanılabilir:

    • bash -x "`which script`" 1 2 3
    • bash -xc '. script' "`which script`" 1 2 3
    • Bu bash script 1 2 3çalışmayan mainbir özellik olarak kabul edilebilir.
  • ( exec -a none script )Çağrıların main( komut dosyasına bashgeçirmediğini unutmayın, bunun için son noktada gösterildiği gibi $0kullanmanız gerekir -c).

Bu nedenle, bazı köşe durumları dışında main, yalnızca komut dosyası olağan şekilde yürütüldüğünde çağrılır. Normalde bu, özellikle de kodu anlamak için karmaşık olmadığı için , istediğiniz şeydir .

Python koduna çok benzediğini unutmayın:

if __name__ == '__main__': main()

Ayrıca main, bazı köşe durumları hariç, çağrıyı önler , çünkü komut dosyasını içe / yükleyebilir ve__name__='__main__'

Neden bu sorunu çözmek için iyi bir genel yol olduğunu düşünüyorum

Birden fazla kabuk tarafından kaynaklanabilecek bir şeyiniz varsa, uyumlu olmalıdır. Ancak, (diğer cevapları okuyun), ing'i tespit etmenin (uygulanması kolay) taşınabilir bir yolu olmadığından source, kuralları değiştirmelisiniz .

Senaryo tarafından yürütülmesi gerektiğini zorlayarak /bin/bash , bunu tam olarak yaparsınız.

Bu , tüm durumları çözer, ancak aşağıdaki durumlarda komut dosyası doğrudan çalışamaz:

  • /bin/bash kurulu değil veya işlevsiz (ör. önyükleme ortamında E.)
  • Eğer bir kabuğa curl https://example.com/script | $SHELL
  • (Not: Bu, yalnızca bashyeterince yeniyseniz geçerlidir . Bu tarifin belirli varyantlar için başarısız olduğu bildirilmektedir. Bu nedenle, durumunuz için çalışıp çalışmadığını kontrol ettiğinizden emin olun.)

Ancak, buna ihtiyaç duyduğunuz herhangi bir gerçek nedeni ve aynı komut dosyasını paralel olarak kaynak yeteneğini düşünemiyorum! Genellikle mainelle yürütmek için sarabilirsiniz . Bunun gibi:

  • $SHELL -c '. script && main'
  • { curl https://example.com/script && echo && echo main; } | $SHELL
  • $SHELL -c 'eval "`curl https://example.com/script`" && main'
  • echo 'eval "`curl https://example.com/script`" && main' | $SHELL

notlar


Ksh ve bash-4.3 için test edilmiştir. Güzel. Diğer cevapların yıllarca oy toplaması nedeniyle, cevabınızın zor bir hayatı olacağı üzücü.
hagello

bu cevap için teşekkür ederim. IF ifadesiyle daha uzun, daha az okunabilir testi takdir ettim, çünkü her iki durumu da en azından sessiz olmayan arıza vermek için kullanmak güzel. Benim durumumda kaynaklanmak için bir komut dosyasına ihtiyacım var ya da başka türlü kaynak kullanmama hatası konusunda kullanıcıyı bilgilendirmek.
Tim Richardson

@Tino: On MacOS,: gelince "sıra farklı kabuklar tarafından kaynaklı olabilir" /bin/shetkili bir şekilde olduğu bashiçin atama POSIX modunda BASH_SOURCE sonları Senaryonu. Diğer kabuklarda ( dash, ksh, zsh), bir dosya argüman olarak ileterek Senaryonu çağırarak kabuk yürütülebilir doğrudan arızalar (örneğin, zsh <your-script>sizin komut yanlışlıkla o olduğunu düşünüyorum yapacak kaynaklı ). (Zaten söz boru tüm kabuklarda, kod arızaları.)
mklement0

@Tino: Bir yana: . <your-script>( Sourcing ) prensip olarak tüm POSIX benzeri kabuklardan çalışsa da , yalnızca bir kabuğa özgü özelliklerin yürütmeyi bozmasını önlemek için komut dosyasının yalnızca POSIX özelliklerini kullanmak üzere açıkça yazılması mantıklıdır. diğer kabuklarda; Bir kullanarak Bash mesele hattı (yerine #!/bin/sh) bu nedenle kafa - en az göze çarpan bir açıklama yoktur. Bunun tersine, betiğinizin yalnızca Bash'ten çalıştırılması gerekiyorsa (yalnızca hangi özelliklerin taşınabilir olmayabileceğini düşünmese bile), Bash olmayan kabuklarda yürütmeyi reddetmek daha iyidir .
mklement0

1
@ mklement0 Tekrar teşekkürler, bir sorun olduğunu not ettim. Diğer okuyucular için: bash v3.x ile kaynak yapıldığında yürütülmemelidir main, ancak bu durumda bunu yapar! Ve kaynaklandığı zaman /bin/sh, yani bash --posixbu durumda da aynı şey olur ve bu da oldukça yanlıştır.
Tino

6

Bu daha sonra komut dosyasında çalışır ve _ değişkenine bağlı değildir:

## Check to make sure it is not sourced:
Prog=myscript.sh
if [ $(basename $0) = $Prog ]; then
   exit 1  # not sourced
fi

veya

[ $(basename $0) = $Prog ] && exit

1
Bence bu cevap burada POSIX ile uyumlu birkaç az kişiden biri. Açık dezavantajları ile dosya adını bilmek zorundasınız ve her iki komut dosyasının da aynı dosya adına sahip olması işe yaramaz.
JepZ

5

BASH'a özel bir cevap vereceğim. Korn kabuğu, üzgünüm. Komut dosyası adınızın include2.sh; Sonra fonksiyonu yapmak içerideinclude2.sh denir am_I_sourced. İşte benim demo sürümü include2.sh:

am_I_sourced()
{
  if [ "${FUNCNAME[1]}" = source ]; then
    if [ "$1" = -v ]; then
      echo "I am being sourced, this filename is ${BASH_SOURCE[0]} and my caller script/shell name was $0"
    fi
    return 0
  else
    if [ "$1" = -v ]; then
      echo "I am not being sourced, my script/shell name was $0"
    fi
    return 1
  fi
}

if am_I_sourced -v; then
  echo "Do something with sourced script"
else
  echo "Do something with executed script"
fi

Şimdi birçok şekilde çalıştırmayı deneyin:

~/toys/bash $ chmod a+x include2.sh

~/toys/bash $ ./include2.sh 
I am not being sourced, my script/shell name was ./include2.sh
Do something with executed script

~/toys/bash $ bash ./include2.sh 
I am not being sourced, my script/shell name was ./include2.sh
Do something with executed script

~/toys/bash $ . include2.sh
I am being sourced, this filename is include2.sh and my caller script/shell name was bash
Do something with sourced script

Yani bu istisnasız çalışıyor ve kırılgan $_şeyleri kullanmıyor . Bu hile BASH'in içgörü tesisini, yani yerleşik değişkenleri FUNCNAMEveBASH_SOURCE ; bkz. bash kılavuz sayfasındaki belgelerine.

Sadece iki uyarı:

1) çağrısına am_I_called gerekir gerçekleşecek içinde kaynaklı senaryo ama değil içinde herhangi bir fonksiyon, diye ${FUNCNAME[1]}başka döner bir şey. Evet ... kontrol edebilirdin ${FUNCNAME[2]}- ama hayatını zorlaştırıyorsun.

2) işlevi am_I_called gerekir Eğer dosyanın adı dahil olmak öğrenmek istiyorsanız kaynaklı komut bulunur.


1
Açıklama: Bu özellik, çalışmak için BASH 3+ sürümünü gerektirir. BASH 2'de, FUNCNAME bir dizi yerine skaler bir değişkendir. Ayrıca BASH 2'de BASH_SOURCE dizi değişkeni yoktur.
Wirawan Purwanto

4

Biraz daha taşınabilir hale getirmek için Dennis'in çok yararlı cevabına küçük bir düzeltme önermek isterim, umarım:

[ "$_" != "$0" ] && echo "Script is being sourced" || echo "Script is a subshell"

çünkü [[(biraz anal retentif IMHO) Debian POSIX uyumlu kabuk tarafından tanınmıyor dash. Ayrıca, yine söz konusu kabukta boşluk içeren dosya adlarına karşı koruma sağlamak için tırnaklara ihtiyaç duyulabilir.


2

$_oldukça kırılgandır. Senaryoda yaptığınız ilk şey olarak kontrol etmelisiniz. Ve o zaman bile, kabuğunuzun adını (kaynaklanmışsa) veya komut dosyasının adını (yürütülüyorsa) içereceği garanti edilmez.

Örneğin, kullanıcı BASH_ENVbir komut dosyasının üst kısmında ayarladıysa , komut dosyasında $_yürütülen son komutun adını içerir BASH_ENV.

Bulduğum en iyi yol şu şekilde kullanmaktır $0:

name="myscript.sh"

main()
{
    echo "Script was executed, running main..."
}

case "$0" in *$name)
    main "$@"
    ;;
esac

Ne yazık ki, bu functionargzeroadın önerdiğinden daha fazlasını yapma seçeneği ve varsayılan olarak açık olması nedeniyle zsh'ta kutudan çıkmıyor .

Bu çevrede çalışmaları için, ben koymak unsetopt functionargzerobenim, .zshenv.


1

Mklement0 kompakt ifadesini takip ettim .

Bu temiz, ama ben böyle çağrıldığında ksh durumunda başarısız olabilir fark ettim:

/bin/ksh -c ./myscript.sh

(kaynaklandığını düşünüyor ve bunun bir alt kabuk çalıştırdığı için değil) Ancak ifade bunu tespit etmek için işe yarayacak:

/bin/ksh ./myscript.sh

Ayrıca, ifade kompakt olsa bile, sözdizimi tüm kabuklarla uyumlu değildir.

Bu yüzden bash, zsh, dash ve ksh için çalışan aşağıdaki kodla bitirdim

SOURCED=0
if [ -n "$ZSH_EVAL_CONTEXT" ]; then 
    [[ $ZSH_EVAL_CONTEXT =~ :file$ ]] && SOURCED=1
elif [ -n "$KSH_VERSION" ]; then
    [[ "$(cd $(dirname -- $0) && pwd -P)/$(basename -- $0)" != "$(cd $(dirname -- ${.sh.file}) && pwd -P)/$(basename -- ${.sh.file})" ]] && SOURCED=1
elif [ -n "$BASH_VERSION" ]; then
    [[ $0 != "$BASH_SOURCE" ]] && SOURCED=1
elif grep -q dash /proc/$$/cmdline; then
    case $0 in *dash*) SOURCED=1 ;; esac
fi

Egzotik kabuk desteği eklemekten çekinmeyin :)


In ksh 93+u, ksh ./myscript.shbenim için iyi çalışıyor (benim beyanı ile) - hangi sürümü kullanıyorsunuz?
mklement0

Ben hiçbir yolu yoktur korku güvenilir bir komut dosyası kullanılarak kaynaklı olup olmadığını belirlemek sadece POSIX-taşıyor: girişimi (Linux varsayar /proc/$$/cmdline) ve odaklanır dash(aynı zamanda gibi davranan sadece shörneğin Ubuntu). Belirli varsayımlar yapmaya hazırsanız $0, portatif olan makul - ancak eksik - bir testi inceleyebilirsiniz.
mklement0

Temel yaklaşım için ++, ancak - benim cevap için bir ek olarak destek sh/ dashde, en iyi taşınabilir yaklaşım olduğunu düşündüğüm için uyarlama özgürlüğü aldım.
mklement0

0

Bunu hem ksh hem de bash'da yapmanın taşınabilir bir yolu olduğunu düşünmüyorum. Bash'da callerçıktı kullanarak tespit edebilirsiniz , ama ksh'da eşdeğer olduğunu düşünmüyorum.


$0çalışır bash, ksh93ve pdksh. ksh88Test etmek zorunda değilim .
Mikel

0

Ben bash.version> = 3 ile [mac, linux] üzerinde çalışan bir astar gerekli ve bu cevapların hiçbiri tasarıyı uygun.

[[ ${BASH_SOURCE[0]} = $0 ]] && main "$@"

1
bashÇözeltisi (size basitleştirmek olabilir iyi çalışıyor $BASH_SOURCE), ancak kshçözüm sağlam değildir: senaryonuz tarafından kaynaklı olup olmadığını başka alfabede , sen yanlış Pozitif alırsınız.
mklement0

0

Doğrudan nokta: "$ 0" değişkeninin Shell adınıza eşit olup olmadığını değerlendirmelisiniz .


Bunun gibi:

#!/bin/bash

echo "First Parameter: $0"
echo
if [[ "$0" == "bash" ]] ; then
    echo "The script was sourced."
else
    echo "The script WAS NOT sourced."
fi


SHELL ile :

$ bash check_source.sh 
First Parameter: check_source.sh

The script WAS NOT sourced.

SOURCE ile :

$ source check_source.sh
First Parameter: bash

The script was sourced.



% 100 taşınabilir bir yol elde etmek oldukça zorBir komut dosyasının kaynaklanıp kaynaklanmadığını saptamak için .

Benim deneyim ile ilgili olarak (Shellscripting ile 7 yaş) (ile ortam değişkenleri güvenerek değil, sadece güvenli bir şekilde PID'lerden bu da bir şeydir olmasından kaynaklanmaktadır güvenli değil ve benzerleri, DEĞİŞKEN ) şunları yapmalısınız:

  • olasılıkları
  • isterseniz anahtarı / kasayı kullanarak.

Her iki seçenek de otomatik olarak ölçeklenemez, ancak daha güvenli bir yoldur.



Örneğin:

bir komut dosyasını bir SSH oturumu aracılığıyla kaynak yaptığınızda, "$ 0" değişkeni ( kaynak kullanırken ) tarafından döndürülen değer -bash olur .

#!/bin/bash

echo "First Parameter: $0"
echo
if [[ "$0" == "bash" || "$0" == "-bash" ]] ; then
    echo "The script was sourced."
else
    echo "The script WAS NOT sourced."
fi

VEYA

#!/bin/bash

echo "First Parameter: $0"
echo
if [[ "$0" == "bash" ]] ; then
    echo "The script was sourced."
elif [[ "$0" == "-bash" ]] ; then
    echo "The script was sourced via SSH session."
else
    echo "The script WAS NOT sourced."
fi

2
Bu yanlış olduğu için aşağıya oy verildi: /bin/bash -c '. ./check_source.sh'verir The script WAS NOT sourced.. Aynı hata: ln -s /bin/bash pumuckl; ./pumuckl -c '. ./check_source.sh'->The script WAS NOT sourced.
Tino

2
Downvote'unuz tüm senaryoyu değiştirdi ve büyük bir katkı yaptı, Tino. Teşekkürler!
ivanleoncz

0

Kontrol ettim [[ $_ == "$(type -p "$0")" ]]

if [[ $_ == "$(type -p "$0")" ]]; then
    echo I am invoked from a sub shell
else
    echo I am invoked from a source command
fi

curl ... | bash -s -- ARGSAnında uzaktan komut dosyası çalıştırmak için kullandığınızda , gerçek komut dosyası çalıştırıldığında $ 0 bashnormal yerine sadece olacak /bin/bash, bu yüzden kullanıyorumtype -p "$0" bash tam yolunu göstermek için .

Ölçek:

curl -sSL https://github.com/jjqq2013/bash-scripts/raw/master/common/relpath | bash -s -- /a/b/c/d/e /a/b/CC/DD/EE

source <(curl -sSL https://github.com/jjqq2013/bash-scripts/raw/master/common/relpath)
relpath /a/b/c/d/e /a/b/CC/DD/EE

wget https://github.com/jjqq2013/bash-scripts/raw/master/common/relpath
chmod +x relpath
./relpath /a/b/c/d/e /a/b/CC/DD/EE

0

Bu, "evrensel" çapraz kabuk desteği ile ilgili diğer bazı cevaplardan bir geri dönüş. Bu, özellikle https://stackoverflow.com/a/2942183/3220983'e çok benzer olsa da, biraz farklıdır. Bunun zayıflığı, bir istemci betiğinin nasıl kullanılacağına (yani önce bir değişkeni dışa aktararak) saygı duyması gerektiğidir. Güç, bunun basit ve "her yerde" çalışması gerektiğidir. Kes ve yapıştır keyfiniz için bir şablon:

# NOTE: This script may be used as a standalone executable, or callable library.
# To source this script, add the following *prior* to including it:
# export ENTRY_POINT="$0"

main()
{
    echo "Running in direct executable context!"
}

if [ -z "${ENTRY_POINT}" ]; then main "$@"; fi

Not: exportBu mekanizmanın alt süreçlere genişletilebildiğinden emin olun.

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.