Soru POSIX'te betik dizini nasıl alınır?


Bash kodumda aşağıdaki kod var. Şimdi POSIX'te kullanmak istiyorum. Peki nasıl dönüştürülür? Teşekkürler.

DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" > /dev/null && pwd )"

30
2018-04-23 18:54


Menşei


Sorun, dizi değil. Sorun var BASH_SOURCE bu bir bash-ism. Başka cevaplar var bu soru ve orada mywiki.wooledge.org/BashFAQ/028 - Etan Reisner
Senin problemin dizilerde olduğu gibi BASH_SOURCE Belli ki, sadece bash. - John Bollinger
Deneyin DIR=$( cd -P -- "$(dirname -- "$(command -v -- "$0")")" && pwd -P ), ne buldum http://stackoverflow.com/questions/760110/can-i-get-the-absolute-path-to-the-current-script-in-kornshell. - Walter A
Ne olduğunu merak ediyorum command Kullanımın orada olması gerekiyordu. Bu, içinde olmayan bir şey için bir hata gibi görünüyor PATH. - Etan Reisner
@EtanReisner: command -v argümanı olduğu sürece çalışacaktır yol bir çalıştırılabilir dosya (aksine dosya adıBu durumda, maçlar $PATH). Ancak, sadece bu yolu yansıtır olduğu gibive zarar görmüyor gibi gözükürken, neden orada olduğu hakkında hiçbir fikrim yok. - mklement0


Cevaplar:


POSIX-kabuğu (sh) $BASH_SOURCE olduğu $0. arka plan bilgisi için aşağıya bakın

Uyarı: Önemli fark şu ki: komut dosyanız çalışıyorsa kaynaklı (içine yüklendi şimdiki kabuk ile .), aşağıdaki snippet'ler değil düzgün çalış.  açıklama aşağıda daha detaylı

Değiştiğime dikkat et DIR için dir Aşağıdaki parçacıklarda, çünkü Büyük harfli değişken isimlerini kullanmama daha iyi çevre değişkenleri ve özel kabuk değişkenleri ile çatışmalardan kaçınmak için.
CDPATH= önek yerini alır > /dev/null Orijinal komutta: $CDPATH emin olmak için boş bir dizeye ayarlanır cd asla yankıları şey.

En basit durumda, bu yapacağız ( OP'nin komutunun karşılığı):

dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)

Eğer sen de istiyorsan çözmek Ortaya çıkan dizin ve / veya bileşenlerinin olması durumunda dizin hedefini nihai hedefine sembolik bağlar, eklemek -P göre pwd komut:

dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd -P)

Uyarı: Bu DEĞİL bulguyla aynı komut dosyasının kendi özgün dizini:
Senaryonuzu diyelim foo simgelenir /usr/local/bin/foo içinde $PATHama gerçek yolu /foodir/bin/foo.
Yukarıdaki rapor yine de /usr/local/binÇünkü sembolik bağlantı çözümü (-P) uygulanır rehber, /usr/local/binbetiğin kendisinden ziyade.

Komut dosyasının kendi özgün dizinini bulmak için, incelemek zorundasın senaryo bir link olup olmadığını görmek için yol ve eğer öyleyse, nihai hedef dosyaya (zincir) symlinks izleyin ve sonra dizin yolunu ayıklayın. hedef dosyanın kanonik yolu.

GNU en readlink -f (daha iyi: readlink -e) bunu senin için yapabilirdim, ama readlink POSIX yardımcı programı değildir.
OSX dahil BSD platformları bir readlink yardımcı programı OSX'de desteklemiyor -fişlevselliği. Bu, görevin ne kadar basit olduğunu göstermek için Eğer  readlink -f gecerli: dir=$(dirname "$(readlink -f -- "$0")"). 

Aslında, orada yok hayır Çözüm için POSIX yardımcı programı dosya sembolik bağlar. Etrafında çalışmak için yollar var, ama hantal ve tamamen sağlam değiller:

takip etme, POSIX uyumlu kabuk işlevi GNU'ları uygular readlink -e yapar ve bir makul derecede sağlam çözüm o sadece iki nadir kenarda başarısız olur:

  • gömülü yollar yeni satırların (çok nadir)
  • değişmez dize içeren dosya isimleri -> (ayrıca nadir)

Bu işlevle adlandırılmış rreadlink, tanımlanmış Aşağıdakiler betiğin gerçek dizin yolunu belirler:

dir=$(dirname -- "$(rreadlink "$0")")

rreadlink()  kaynak kodu - yer önce çağırır komut dosyalarında:

rreadlink() ( # Execute the function in a *subshell* to localize variables and the effect of `cd`.

  target=$1 fname= targetDir= CDPATH=

  # Try to make the execution environment as predictable as possible:
  # All commands below are invoked via `command`, so we must make sure that `command`
  # itself is not redefined as an alias or shell function.
  # (Note that command is too inconsistent across shells, so we don't use it.)
  # `command` is a *builtin* in bash, dash, ksh, zsh, and some platforms do not even have
  # an external utility version of it (e.g, Ubuntu).
  # `command` bypasses aliases and shell functions and also finds builtins 
  # in bash, dash, and ksh. In zsh, option POSIX_BUILTINS must be turned on for that
  # to happen.
  { \unalias command; \unset -f command; } >/dev/null 2>&1
  [ -n "$ZSH_VERSION" ] && options[POSIX_BUILTINS]=on # make zsh find *builtins* with `command` too.

  while :; do # Resolve potential symlinks until the ultimate target is found.
      [ -L "$target" ] || [ -e "$target" ] || { command printf '%s\n' "ERROR: '$target' does not exist." >&2; return 1; }
      command cd "$(command dirname -- "$target")" # Change to target dir; necessary for correct resolution of target path.
      fname=$(command basename -- "$target") # Extract filename.
      [ "$fname" = '/' ] && fname='' # !! curiously, `basename /` returns '/'
      if [ -L "$fname" ]; then
        # Extract [next] target path, which may be defined
        # *relative* to the symlink's own directory.
        # Note: We parse `ls -l` output to find the symlink target
        #       which is the only POSIX-compliant, albeit somewhat fragile, way.
        target=$(command ls -l "$fname")
        target=${target#* -> }
        continue # Resolve [next] symlink target.
      fi
      break # Ultimate target reached.
  done
  targetDir=$(command pwd -P) # Get canonical dir. path
  # Output the ultimate target's canonical path.
  # Note that we manually resolve paths ending in /. and /.. to make sure we have a normalized path.
  if [ "$fname" = '.' ]; then
    command printf '%s\n' "${targetDir%/}"
  elif  [ "$fname" = '..' ]; then
    # Caveat: something like /var/.. will resolve to /private (assuming /var@ -> /private/var), i.e. the '..' is applied
    # AFTER canonicalization.
    command printf '%s\n' "$(command dirname -- "${targetDir}")"
  else
    command printf '%s\n' "${targetDir%/}/$fname"
  fi
)

Güçlü ve öngörülebilir olması için işlev kullanılır command Yalnızca kabuk yapıları veya harici yardımcı programların çağrıldığından emin olmak için (diğer adlar ve işlevler biçimindeki aşırı yükleri yoksayar).
Oldu Aşağıdaki kabukların son sürümlerinde test edilmiştir: bash, dash, ksh, zsh.


Nasıl ele alınır kaynaklı yürütme:

tl; Dr.:

kullanma Yalnızca POSIX özellikleri:

  • Sen yapamam komut dosyasının yolunu belirle içinde kaynaklı yakarma (hariç zshBununla birlikte, genellikle, sh).
  • Sen kutu olup olmadığını tespit etmek komut dosyanız kaynak kaynaklıysa SADECE kaynaklanıyor direkt olarak kabuk tarafından (Kabuk profili / başlatma dosyasında olduğu gibi; Zincir Karşılaştırma yaparak) $0 kabuğun çalıştırılabilir ismine / yoluna (hariç zsh, not edildiği gibi, $0 gerçekten geçerli komut dosyasının yolu. Aksine (hariç zsh), gelen bir komut dosyası başka bir senaryo kendisi doğrudan çağrıldıiçerir o komut dosyasının yolu $0.
  • Bu sorunları çözmek için bash, ksh, ve zsh var Standart dışı Özellikler o yap kaynaklı senaryolarda bile gerçek senaryo yolunu belirlemeyi ve ayrıca bir komut dosyasının kaynaklanıp kaynaklanmadığını tespit etme; örneğin bash, $BASH_SOURCE  her zaman çalışan komut dosyasının yolunu, kaynak olup olmadığını, ve [[ $0 != "$BASH_SOURCE" ]] komut dosyasının kaynak olup olmadığını sınamak için kullanılabilir.

Bunun neden yapılamadığını göstermek için, komuta analiz edelim Walter A'nın cevabı:

    # NOT recommended - see discussion below.
    DIR=$( cd -P -- "$(dirname -- "$(command -v -- "$0")")" && pwd -P )
  • (İki aside:
    • kullanma -P iki kez gereksizdir - onu kullanmak için yeterlidir pwd.
    • Komutun suskunluğu yok cdpotansiyel stdout çıkışı, eğer $CDPATH ayarlanmış olur.)
  • command -v -- "$0"
    • command -v -- "$0" bir ek senaryoyu kapsayacak şekilde tasarlanmıştır: komut dosyası çalışıyorsa kaynaklı bir interaktif kabuk, $0 genellikle içerir sadece dosya adı Kabuk yürütülebilir (sh), bu durumda dirname sadece dönecekti . (çünkü böyle dirname değişmez bir argüman verildiğinde yol bileşen). command -v -- "$0" sonra o kabuğun mutlak yolunu bir $PATH yukarı Bak (/bin/sh). Ancak şunu unutmayın oturum aç Bazı platformlardaki kabuklar (ör. OSX), dosya adlarının öneki ile - içinde $0 (-sh), bu durumda command -v -- "$0" amaçlandığı gibi çalışmıyor (bir boş dize).
    • Tersine, command -v -- "$0" iki yanlış yaratabilir olmayankaynaklı senaryolar Kabuğun çalıştırılabilir olduğu sh, direkt olarak komut dosyasıyla bir argüman olarak çağrıldı:
      • komut dosyasının kendisi ise değilyürütülebilir: command -v -- "$0" bir iade edebilir boş dizeözel kabuk ne gibi davranır sh belirli bir sistemde: bash, ksh, ve zsh boş bir dize döndürmek; bir tek dash yankıları $0
        POSIX özellikleri için command açıkça belirtmiyor mu command -v, uygulandığında dosya sistemi yolu, sadece geri dönmeli yürütülebilir dosyalar - hangisi bash, ksh, ve zsh Yapmak - ama bunun çok amaçlı olduğunu ima edebilirsiniz command; merakla, dashGenellikle en uygun POSIX vatandaşı olan buradaki standarttan sapıyor. Aksine, ksh Burada yalnız model vatandaş, çünkü sadece yürütülebilir dosyaları rapor eden tek ve onları bir raporla kesin (gerektiği gibi değil) yolu, spec gerektiği gibi.
      • eğer senaryo olduğu yürütülebilir, ancak $PATHve onun yerini kullanır sadece dosya adı (Örneğin., sh myScript), command -v -- "$0" ayrıca iade edecek boş dize, hariç dash.
    • Komut dosyasının dizini verildiğinde yapamam betik olduğunda belirlenir kaynaklı - Çünkü $0 o zaman bu bilgiyi içermez ( zshgenellikle davranmaz sh) - Bu problem için iyi bir çözüm yok.
      • Geri dönen kabuk yürütülebilir Bu durumda dizin yolu sınırlı kullanım - her şeyden önce değil, senaryo dizini - belki de daha sonra bu yolu kullanmak için Ölçek belirlemek, birsey belirlemek öyle ya da böyle betik kaynaklanıyor.
        • Daha güvenilir bir yaklaşım basitçe test etmek olurdu $0 direkt olarak: [ "$0" = "sh" ] || [ "$0" = "-sh" ] || [ "$0" = "/bin/sh" ]
      • Ancak, bu komut dosyası kaynaklıysa bile işe yaramıyor. bir diğeri komut dosyası (kendisi direkt olarak çünkü) $0 sonra sadece içerir kaynak komut dosyası yolu.
    • Sınırlı yararı göz önüne alındığında command -v -- "$0" kaynaklı senaryolarda ve iki kırdığı gerçeği olmayankaynaklı senaryolar oy kullanmamak için oy kullanıyorum, ile bizi bırakır:
      • Herşey olmayankaynaklı senaryolar Hangi kapalı.
      • İçinde kaynaklı davetler, sen komut dosyasının yolunu belirleyemiyorve en azından, sınırlı durumlarda, yapabilirsiniz olup olmadığını tespit etmek kaynak kullanımı gerçekleşiyor:
        • Ne zaman direkt olarak kabuk tarafından (kabuk profili / başlatma dosyasından olduğu gibi), $dir içeren ya da biter .Kabuk yürütülebilir dosya sadece bir dosya adı olarak çağrıldıysa dirname sadece bir dosya adına her zaman döner .) veya kabuk yürütülebilir dosyasının dizin yolu. . Mevcut dizinden kaynaklı olmayan bir çağrıda güvenilir şekilde ayırt edilemez.
        • Kaynaklı olduğunda başka bir senaryo (kendisi de kaynak değildi), $0 içeren o komut dosyasının yolu ve kaynaklı olan komut dosyasının durumun böyle olup olmadığını anlatmanın bir yolu yoktur.

Arkaplan bilgisi:

POSIX tanımlar davranışları $0 kabuğa göre senaryo  İşte.

esasen, $0 komut dosyasının yolunu yansıtmalıdır belirtildiği gibi, Hangi ima:

  • Güvenmeyin $0 içeren kesin yol.
  • $0 bir içerir kesin yol Yalnızca:

    • sen açıkça belirtmek kesin yolu; Örneğin.:
      • ~/bin/myScript (betiğin kendisi yürütülebilir olduğu varsayılarak)
      • sh ~/bin/myScript
    • tarafından bir çalıştırılabilir komut dosyası çağırırsınız sadece dosya adıHer ikisinin de çalıştırılabilir olmasını gerektirir ve içinde $PATH; perde arkasında, sistem dönüşüyor myScript mutlak bir yol içine ve sonra yürütür; Örneğin.:
      • myScript # executes /home/jdoe/bin/myScript, for instance
  • Diğer tüm durumlarda, $0 senaryoyu yansıtacak yol belirtildiği gibi:

    • Açıkça çağrıldığında sh bir senaryo ile, bu bir sadece dosya adı (Örneğin., sh myScript) veya göreceli yol (Örneğin., sh ./myScript)
    • Bir çalıştırılabilir komut dosyasını çağırırken direkt olarak, bu bir olabilir bağıl yol (ör. ./myScript - buna dikkat et sadece dosya adı sadece betikleri bulur içinde $PATH).

Uygulamada, bash, dash, ksh, ve zsh hepsi bu davranışı sergilemektedir.

Aksine, POSIX değerini kabul ETMEZ $0 ne zaman kaynak Bir komut dosyası (özel yerleşik yardımcı programı kullanarak . ("nokta")) ona güvenemezsinve pratikte davranış kabukları arasında farklılık gösterir.

  • Böylece, körü körüne kullanamazsınız $0 betiğiniz kaynaklandığında ve standart davranış beklediğinde.
    • Uygulamada, bash, dash, ve ksh ayrılmak $0  dokunulmamış scripts kaynaklandığında, bunun anlamı $0 içerir arayan en  $0 değer veya daha doğrusu, $0 arama zincirindeki en yeni arayıcının değeri, kendisinden kaynaklanmadı; Böylece, $0 kabuğun yürütülebilir dosyasına veya yoluna işaret edebilir bir diğeri Geçerli olanı oluşturan (doğrudan çağrılan) komut dosyası.
    • Aksine, zshYalnız bir muhalif olarak yapar rapor et şimdiki komut dosyasının yolu $0. Tersine, $0 Komut dosyasının kaynak olup olmadığına dair herhangi bir bilgi vermez.
    • Kısacası: sadece POSIX özelliklerini kullanarak, eldeki komut dosyasının kaynaklanıp kaynaklanmadığını veya eldeki yoldaki komut dosyasının ne olduğunu ve ne ile ilişki kurduğunu güvenilir şekilde söyleyemezsiniz. $0 mevcut komut dosyasının yoludur.
  • Bu durumu ele almanız gerekiyorsa, belirli bir kabuğu elinizde tespit etmeli ve özel standart olmayan özellikler:
    • bash, ksh, ve zsh hepsi teklif kendi Çalıştırılırken bile, çalışan komut dosyasının yolunu edinme yolları.

Bütünlük uğruna: değeri $0 içinde diğer bağlamlar:

  • Bir kabuk içinde fonksiyon, POSIX bunu zorunlu kılıyor $0 kalmak değişmemiş; bu nedenle, sahip olduğu değer ne olursa olsun dışında fonksiyon, olacak içeride de.
    • Uygulamada, bash, dash, ve ksh bu şekilde davranmak.
    • Tekrar, zsh yalnız muhalif ve bildiriyor Fonksiyonunuzdaad.
  • Kabul eden bir kabukta üzerinden komut dizesi -c seçenek başlangıçta, bu ilk işlenen (seçenek olmayan argüman) $0; Örneğin.:
    • sh -c 'echo \$0: $0 \$1: $1' foo one # -> '$0: foo $1: one'
    • bash, dash, ksh, ve zsh hepsi bu şekilde davranır.
  • Aksi takdirdebir kabukta değil yürütmek Komut dosyaları, $0 kabuğun ilk argümanının değeridir ana süreç geçti - tipik olarak, bu kabuk en ad veya yol (ör. shveya /bin/sh); bu içerir:
    • bir interaktif kabuk
      • Uyarı: Bazı platformlar, özellikle OSX, her zaman yarat oturum aç etkileşimli kabuklar oluştururken kabuklar, ve başa getirebilir - yerleştirmeden önce kabuk adına $0Kabuğa bir _login kabuğu olduğu sinyalini vermek için; bu nedenle, varsayılan olarak, $0 raporlar -bash, değil bashOSX'deki etkileşimli kabuklarda.
    • okuyan bir kabuk komutları stdin
      • Bu, aynı zamanda, bir komut dosyasını stdin yoluyla kabuğa borulamak için de geçerlidir (ör. sh < myScript)
    • bash, dash, ksh, ve zsh hepsi bu şekilde davranır.

52
2018-04-23 22:26



Bu bir Tag wiki adayı. İyi cevap. - David C. Rankin
Teşekkürler, DavidC.Rankin. - mklement0
Müthiş heyecanlanıyorum! Yazık bir çok kez oy veremem! - Don Question
Bu detaylı bir cevap ... vay. - James Ko
Güzel cevap! - Rany Albeg Wein


@City buna cevap verdi

DIR=$( cd -P -- "$(dirname -- "$(command -v -- "$0")")" && pwd -P )

Eserleri. Bende kullandım.
Şu komutu buldum: https://stackoverflow.com/questions/760110/can-i-get-the-absolute-path-to-the-cu rrent-komut-in-kornshell.


0
2018-04-23 21:36



Zarar vermemekle birlikte genel olarak diğer yığın yayınlara yorumlarda başvurulur, cevap olarak gönderilmez. Bağlantıyı sağlamak için SO formatı [Link Title] (url) 'dir. (Çift Başlı Yıldızlarla Bağlantı Başlığını çevreleyen normal kalın harfleri ekleyebilirsiniz). KornShell'deki mevcut betiğin mutlak yolunu bulabilir miyim? - David C. Rankin
@David Biçim için teşekkürler, önce yorum yazdım. Yorumumun işe yaradığına dair bir cevabım vardı, bu yüzden soruyu kapatma olanağı sunmak istedim. Diğer insanlar, şehrin hala bir cevap aradığını düşünüyor olabilir. Bunu nasıl yapmam gerektiğini tavsiye edebilir misin? - Walter A
Yorum çalışsa bile, yorumda bir referans olarak bırakılmalıdır. Niye ya? Çünkü cevabınız çoktan, ve SO üzerinde büyük bir baş ağrısının, farklı soruların altında SO'nın her yerine dağılmış aynı cevabın ihmal edilebilir farklı versiyonları var. Amaç, tüm ilgili cevapları tek bir soru altında ele almaktır, böylece herkes için kolayca bulunur / referans alınır. (Asla% 100'e varamayacağız, ama cevabı başka bir soru altında bulduğunuzda, lütfen tekrar çoğaltmayın) - David C. Rankin
@ DavidC.Rankin: Bu sorunun açıkça hakkında olduğunu unutmayın. POSIX kabuğu (sh), burada bağlantılı olan soru hakkında kshve tepedeki yorumlarda bağlantılı sorular bash sorular; süre bazı Bu soruların cevaplarının da POSIX uyumlu olan çözümleri içermesi, böyle tartışılmaması, ve bu sorunun yakından ilişkili olmasına rağmen, bu genel bir ilgiden ibarettir ve kendi başına, kanonik bir cevabı hak eder. . Belki de Walter, ksh etiketli çözümü kopyalayabilirdi ve açıkladı neden çalışır herşey POSIX uyumlu mermiler. - mklement0