Kabuk ad alanları


10

sourceBir ad alanı, tercihen bir bash kabuk komut dosyası içine bir kabuk komut dosyası için bir yol var mı ama bu özelliği varsa ve bash yoktu diğer kabukları bakmak istiyorum.

Bununla kastettiğim, örneğin "önceden tanımlanmış sembollerle (değişken adları, işlev adları, takma adlar) çarpışmamaları için" tüm tanımlanmış sembollere bir şey önek "veya ad çakışmalarını önleyen başka bir tesis.

Eğer sourcezaman ( NodeJSstil) ad alanı yapabileceğim bir çözüm varsa , bu en iyisi olurdu.

Örnek kod:

$ echo 'hi(){ echo Hello, world; }' > english.sh
$ echo 'hi(){ echo Ahoj, světe; }' > czech.sh
$ . english.sh
$ hi
 #=> Hello, world
$ . czech.sh #bash doesn't even warn me that `hi` is being overwritten here
$ hi
 #=> Ahoj, světe
#Can't use the English hi now
#And sourcing the appropriate file before each invocation wouldn't be very efficient 

1
Açıklama için teşekkürler. Cevabın olumsuz olmasını bekliyorum. Her zamanki kabuk programlama paradigması, değişiklikleri izole etmek istediğinizde, bunu oluşturmak için bir alt kabukta yaptığınızdır ( easiest thing ever ). Ama peşinde olduğunuz bu değil. Sanırım yapabilirsin ( stuff in subshell; exec env ) | sed 's/^/namespace_/'ve evalana kabukta sonuç ama bu biraz kötü.
Celada

3
Evet. Alın ksh93. Ad alanları bunun için temeldir ve tüm ad türleri (bunlar da yazılabilir) ad aralığını destekler. Aynı zamanda pratik olarak her açıdan bashbu arada çok daha hızlı .
mikeserv

@mikeserv Teşekkürler, işlevselliği gösteren bir kod örneğiyle yanıt olarak eklerseniz, kabul edeceğim.
PSkocik

@michas Ben de işlev sembolleri ve takma ad isim gerekir. env | sed ...Değişkenler için işe yarayabilir, setişlevleri almak için yapabilirim , ancak arama ve değiştirme bir sorun olurdu - işlevler birbirini arayabilir ve böylece tüm çapraz çağrıları önceden eklenmiş çapraz çağrılarla değiştirmelisiniz, ancak fonksiyon tanımlama kodunun başka bir yerinde aynı kelimeler, burada bir çağırma değildir. Bunun için sadece bir regex değil, bir bash ayrıştırıcıya ihtiyacınız olacak ve işlevler eval yoluyla birbirini çağırmadıkça hala işe yarayacaktır.
PSkocik

Yanıtlar:


11

Gönderen man kshbir ile bir sistemde ksh93yüklü ...

  • İsim Alanları
    • namespaceDeğişkenleri değiştiren veya yenilerini oluşturan bir komut listesinin bir parçası olarak yürütülen komutlar ve işlevler , adının başında tanımlayıcı tarafından verilen ad boşluğunun adı olan yeni bir değişken oluşturur .. Adı adı olan bir değişkene başvuruda bulunulduğunda, ilk önce kullanılarak aranır .identifier.name.
    • Benzer şekilde, ad alanı listesindeki bir komutla tanımlanan bir işlev, önünde a ile başlayan ad alanı kullanılarak oluşturulur ..
    • Bir ad alanı komutunun listesi bir komut içerdiğinde namespace, oluşturulan değişkenlerin ve işlevlerin adları, her birinin öncesinde tanımlayıcılar listesinden önce gelen değişken veya işlev adından oluşur .. Bir ad alanının dışında, ad alanının içinde oluşturulan bir değişkene veya işleve, ad alanı adından önce gelerek başvurulabilir.
    • Varsayılan olarak, birlikte bakan değişkenler .shiçindedir shadı alanı.

Ve, göstermek için, burada, bir ksh93kabuğa atanan her normal kabuk değişkeni için varsayılan olarak sağlanan bir ad alanına uygulanan konsept . Aşağıdaki örnekte , kabuk değişkeni için disciplineatanan .getyöntem olarak işlev görecek bir işlev tanımlayacağım $PS1. Her kabuk değişkeni temelde varsayılan, en azından kendi ad alır get, set, append, ve unsetyöntemler. Aşağıdaki işlevi tanımladıktan sonra, değişkenin $PS1kabuğa her başvuruda bulunulduğunda, çıktısı dateekranın üstünde çizilir ...

function PS1.get {
    printf "\0337\33[H\33[K%s\0338" "${ date; }"
}

( ()Yukarıdaki komut ikamesinde alt kabuğun eksikliğine de dikkat edin )

Teknik olarak, ad ve disiplinler değildir tam olarak aynı şey (çünkü disiplinleri belli etmek global veya yerel olarak uygulamak için tanımlanabilir ad ) , ama onlar için esastır kabuk veri türlerinin kavramlaştırmayı hem bir parçasını oluşturuyor ksh93.

Belirli örneklerinizi ele almak için:

echo 'function hi { echo Ahoj, světe\!;  }' >  czech.ksh
echo 'function hi { echo Hello, World\!; }' >english.ksh
namespace english { . ./english.ksh; }
namespace czech   { . ./czech.ksh;   }
.english.hi; .czech.hi

Hello, World!
Ahoj, světe!

...veya...

for ns in czech english
do  ".$ns.hi"
done

Ahoj, světe!
Hello, World!

@PSkocik - neden benim ehoj'umu düzeltmedin ? Daha önce söylediği gibi yemin edebilirdim ... bunun için üzgünüm. Soruda kullandığım kelimeleri doğru bir şekilde yazmayı bile zahmet etmeyen biri tarafından yazılmış bir cevabı kabul etmezdim ...
mikeserv

2

Ben yerel olarak herhangi bir kabuk yerleşiği veya işlevi ad alanı kullanılabilecek bir POSIX kabuk işlevi yazdım ksh93, dash, mkshveya bash (ben şahsen bu tümünde çalışmalarına doğruladı çünkü özellikle adlandırılır) . Test ettiğim mermilerden sadece beklentilerimi karşılayamadı yashve hiç çalışmasını beklemiyordum zsh. Test etmedim posh. poshBir süredir umut verdim ve bir süredir kurmadım. Belki de çalışır posh...?

POSIX olduğunu söylüyorum, çünkü spesifikasyonu okuduğumda, temel bir yardımcı programın belirli bir davranışından yararlanır, ancak, kuşkusuz, şartname bu konuda belirsizdir ve en azından bir kişi görünüşe göre benimle aynı fikirde değil. Genelde bu konuda bir anlaşmazlık yaşadım ve sonunda hatayı kendim buldum ve muhtemelen bu sefer şartname hakkında da yanılıyorum, ama onu daha fazla sorguladığımda cevap vermedi.

Söylediğim gibi, bu kesinlikle yukarıda belirtilen kabuklarda işe yarıyor ve temel olarak şu şekilde çalışıyor:

some_fn(){ x=3; echo "$x"; }
x=
x=local command eval some_fn
echo "${x:-empty}"

3
empty

commandKomut, bir temel olarak kullanılabilir yarar ve ön olarak belirtilir $PATHd' builtins. Belirtilen işlevlerinden biri, özel yerleşik yardımcı programları çağırırken kendi ortamına sarmaktır ve böylece ...

{       sh -c ' x=5 set --; echo "$x"
                x=6 command set --; echo "$x"
                exec <"";  echo uh_oh'
        sh -c ' command exec <""; echo still here'
}

5
5
sh: 3: cannot open : No such file
sh: 1: cannot open : No such file
still here

... yukarıdaki her iki komut satırı atamasının davranışı spec tarafından doğrudur. Her iki hata koşulunun davranışı da doğrudur ve aslında şartnameden neredeyse tamamen çoğaltılmıştır. İşlevlerin veya özel yerleşiklerin komut satırlarına önek atanan atamalar, geçerli kabuk ortamını etkileyecek şekilde belirtilir. Benzer şekilde, yönlendirme hataları bunlardan birine işaret edildiğinde ölümcül olarak belirtilir. commandbu durumlarda özel yerleşiklerin özel muamelesini bastırdığı belirtilmiştir ve yönlendirme durumu aslında spesifikasyonda örnek olarak gösterilmiştir.

commandÖte yandan, normal yerleşiklerin, bir alt kabuk ortamında çalışacağı belirtilir - bu, başka bir işlemin olması gerektiği anlamına gelmez , sadece birinden temel olarak ayırt edilemez olması gerektiği anlamına gelir . Düzenli bir yerleşkeyi çağırmanın sonuçları her zaman benzer yetenekli bir $PATHkomuttan elde edilebilecek şeylere benzemelidir . Ve bu yüzden...

na=not_applicable_to_read
na= read var1 na na var2 <<"" ; echo "$var1" "$na" "$var2"
word1 other words word2

word1 not_applicable_to_read word2

Ancak commandkomut kabuk işlevlerini çağıramaz ve bu nedenle düzenli yerleşikler için olduğu gibi özel tedavi tartışmalarını oluşturmak için kullanılamaz. Bu da belirtilmiş. Aslında, spec bir birincil yardımcı programı, commandişlevi çağırmaz çünkü öz-özyineleme olmadan diğer komutu çağırmak için başka bir komut adlı bir sarıcı kabuk işlevi içinde kullanabileceğiniz olduğunu söylüyor. Bunun gibi:

cd(){ command cd -- "$1"; }

commandOrada kullanmadıysanız , bu cdişlev kendi kendine tekrarlama için neredeyse kesinlikle segfault olur.

Ancak, özel yerleşikleri çağırabilen normal bir yerleşik olarak commandbunu bir alt kabuk ortamında yapabilirsiniz . Ve böylece, içinde tanımlanan mevcut kabuk durumu geçerli kabukta yapışabilirken - kesinlikle read's $var1ve $var2yaptım - en azından komut satırı tanımlarının sonuçları muhtemelen ...

Basit Komutlar

Herhangi bir komut adı yoksa veya komut adı özel bir yerleşik veya işlevse, değişken atamaları geçerli yürütme ortamını etkiler. Aksi takdirde, değişken atamalar komutun yürütme ortamı için dışa aktarılır ve geçerli yürütme ortamını etkilemez.

Şimdi olsa da olmasa commandnormal yerleşik olarak kalacaktır 'ın yeteneği ve doğrudan özel yerleşiklerinden aramaya bilmiyorum komut satırı tanımlayıp bakımından beklenmedik kaçamak sadece bazı tür, ama o en az dört kabukları zaten biliyoruz adı alan onur bahsetti command.

Gerçi Ve commanddoğrudan kabuk işlevleri çağırmak olamaz, bu olabilir çağrı evalgösterdiği gibi, ve böylece bu yüzden dolaylı yapabilirsiniz. Bu yüzden bu konsepte bir ad alanı sarmalayıcısı yaptım. Aşağıdaki gibi bir argüman listesi alır:

ns any=assignments or otherwise=valid names which are not a command then all of its args

... ancak commandyukarıdaki kelime yalnızca boş olarak bulunabiliyorsa tek kelime olarak tanınır $PATH. Ayrıca komut satırında adlı kabuk değişkenleri yerel olarak kapsam, aynı zamanda gibi tek küçük harfle alfabetik isim ve diğer standart olanlardan listesiyle tüm değişken yerel kapsamları $PS3, $PS4, $OPTARG, $OPTIND, $IFS, $PATH, $PWD, $OLDPWDve diğerleri.

Ve evet, lokal kapsamlar tarafından $PWDve $OLDPWDdaha sonra değişkenleri ve açıkça cdiçin ing $OLDPWDve $PWDolabildiğince oldukça güvenilir bir kapsam Geçerli çalışma dizini de. Oldukça zor olsa da, bu garanti edilmez. 7<.Sarma hedefi döndüğünde ve döndüğünde bir tanımlayıcı tutar cd -P /dev/fd/7/. Geçerli çalışma dizini unlink()aradaysa, yine de en azından geri dönüş yapmayı başarmalıdır, ancak bu durumda çirkin bir hata verecektir. Ve tanımlayıcıyı koruduğundan, aklı başında bir çekirdeğin kök cihazının da sökülmesine izin vereceğini düşünmüyorum (???) .

Ayrıca kabuk seçeneklerini yerel olarak kapsamlar ve bunları, sarılı yardımcı programı geri döndüğünde bulduğu duruma geri yükler. $OPTSÖzel olarak, başlangıçta değerini atadığı bir kopyasını kendi kapsamı içinde tutması bakımından davranır $-. Komut satırındaki tüm atamaları da işledikten sonra set -$OPTS, kaydırma hedefini çağırmadan hemen önce yapacaktır . Bu şekilde -$OPTSkomut satırında tanımlarsanız, kaydırma hedefinizin kabuk seçeneklerini tanımlayabilirsiniz. Hedef döndüğünde set +$- -$OPTS, kendi kopyasıyla $OPTS (komut satırından tanımlanmadan etkilenmez) ve tümünü orijinal durumuna geri yükler.

Elbette, arayanın returrnsarma hedefi veya argümanları aracılığıyla bir şekilde açıkça işlevden çıkmasını engelleyen hiçbir şey yoktur . Aksi takdirde, herhangi bir durumun geri yüklenmesini / temizlenmesini önler.

Tüm bunları yapmak için üçün evalderine gitmesi gerekiyor . İlk önce kendini yerel bir kapsamda sarar, sonra içeriden argümanlarda okur, geçerli kabuk isimleri için doğrular ve olmayan birini bulursa hatayla çıkar. Tüm bağımsız değişkenler geçerli ve sonunda tek nedeni ise command -v "$1"gerçek dönmek için (hatırlama: $PATHbu noktada boş) o olacak evalkomut satırı tanımlar ve sarma hedefe kalan tüm argümanları geçmek onun için özel bir durum yok sayar rağmen ( ns- o almadı diye çok kullanışlı olmayın ve üç evals derinlik yeterince derindir) .

Temel olarak şu şekilde çalışır:

case $- in (*c*) ... # because set -c doesnt work
esac
_PATH=$PATH PATH= OPTS=$- some=vars \
    command eval LOCALS=${list_of_LOCALS}'
        for a do  i=$((i+1))          # arg ref
              if  [ "$a" != ns ]  &&  # ns ns would be silly
                  command -v "$a" &&
              !   alias "$a"          # aliases are hard to run quoted
        then  eval " PATH=\$_PATH OTHERS=$DEFAULTS $v \
                     command eval '\''
                             shift $((i-1))         # leave only tgt in @
                             case $OPTS in (*different*)
                                  set \"-\${OPTS}\" # init shell opts 
                             esac
                             \"\$@\"                # run simple command
                             set +$- -$OPTS "$?"    # save return, restore opts
                     '\''"
              cd -P /dev/fd/7/        # go whence we came
              return  "$(($??$?:$1))" # return >0 for cd else $1
        else  case $a in (*badname*) : get mad;;
              # rest of arg sa${v}es
              esac
        fi;   done
    ' 7<.

Bazı kabukları koymak yolu ile yapmak diğer bazı yönlendirmeler ve ve birkaç garip testlerdir ciçinde $-ve daha sonra bir seçenek olarak bunu kabul etmeyi reddediyorum set (???) , ama onun tüm yan ve öncelikle yayan gelen kurtarmak için sadece kullanılan istenmeyen durumlarda ve benzeri durumlarda istenmeyen çıktı. Ve işte böyle çalışır. Bu şeyleri yapabilir çünkü sarılmış yardımcı programını iç içe bir şekilde çağırmadan önce kendi yerel kapsamını kurar.

Uzun, çünkü burada çok dikkatli olmaya çalışıyorum - üçü evalszor. Ancak bununla şunları yapabilirsiniz:

ns X=local . /dev/fd/0 <<""; echo "$X" "$Y"
X=still_local
Y=global
echo "$X" "$Y"

still_local global
 global

Bir adım daha ileri götürmek ve sarılmış yardımcı programın yerel kapsamını kalıcı olarak adlandırmak çok zor olmamalıdır. Ve yazıldığında bile $LOCALS, sarılmış yardımcı program için, yalnızca sarılı yardımcı program ortamında tanımladığı tüm adların boşlukla ayrılmış bir listesinden oluşan bir değişkeni tanımlar .

Sevmek:

ns var1=something var2= eval ' printf "%-10s%-10s%-10s%s\n" $LOCALS '

... tamamen güvenli - $IFSvarsayılan değerine uygun hale getirildi ve sadece geçerli kabuk isimleri $LOCALS, komut satırında kendiniz ayarlamadığınız sürece bunu yapar. Bölünmüş bir değişkente glob karakterleri olsa bile OPTS=f, kaydırılan yardımcı programın genişlemesini yasaklaması için komut satırında da ayarlayabilirsiniz . Her durumda:

LOCALS    ARG0      ARGC      HOME
IFS       OLDPWD    OPTARG    OPTIND
OPTS      PATH      PS3       PS4
PWD       a         b         c
d         e         f         g
h         i         j         k
l         m         n         o
p         q         r         s
t         u         v         w
x         y         z         _
bel       bs        cr        esc
ht        ff        lf        vt
lb        dq        ds        rb
sq        var1      var2      

Ve işte fonksiyon. Genişletmeleri \önlemek için tüm komutlara w / öneki eklenir alias:

ns(){  ${1+":"} return
       case  $- in
       (c|"") ! set "OPTS=" "$@"
;;     (*c*)  ! set "OPTS=${-%c*}${-#*c}" "$@"
;;     (*)      set "OPTS=$-" "$@"
;;     esac
       OPTS=${1#*=} _PATH=$PATH PATH= LOCALS=     lf='
'      rb=\} sq=\' l= a= i=0 v= __=$_ IFS="       ""
"      command eval  LOCALS=\"LOCALS \
                     ARG0 ARGC HOME IFS OLDPWD OPTARG OPTIND OPTS     \
                     PATH PS3 PS4 PWD a b c d e f g h i j k l m n     \
                     o p q r s t u v w x y z _ bel bs cr esc ht ff    \
                     lf vt lb dq ds rb sq'"
       for a  do     i=$((i+1))
              if     \[ ns != "$a" ]         &&
                     \command -v "$a"  >&9   &&
              !      \alias "${a%%=*}" >&9 2>&9
              then   \eval 7>&- '\'    \
                     'ARGC=$((-i+$#))  ARG0=$a      HOME=~'           \
                     'OLDPWD=$OLDPWD   PATH=$_PATH  IFS=$IFS'         \
                     'OPTARG=$OPTARG   PWD=$PWD     OPTIND=1'         \
                     'PS3=$PS3 _=$__   PS4=$PS4     LOCALS=$LOCALS'   \
                     'a= b= c= d= e= f= g= i=0 j= k= l= m= n= o='     \
                     'p= q= r= s= t= u= v= w= x=0 y= z= ht=\   '      \
                     'cr=^M bs=^H ff=^L vt=^K esc=^[ bel=^G lf=$lf'   \
                     'dq=\" sq=$sq ds=$ lb=\{ rb=\}' \''"$v'          \
                            '\command eval       9>&2 2>&- '\'        \
                                   '\shift $((i-1));'                 \
                                   'case \${OPTS##*[!A-Za-z]*} in'    \
                                   '(*[!c$OPTS]*) >&- 2>&9"'\'        \
                                   '\set -"${OPTS%c*}${OPTS#*c}"'     \
                                   ';;esac; "$@" 2>&9 9>&-; PS4= '    \
                                   '\set  +"${-%c*}${-#*c}"'\'\"      \
                                          -'$OPTS \"\$?\"$sq";'       \
              '             \cd -- "${OLDPWD:-$PWD}"
                            \cd -P  ${ANDROID_SYSTEM+"/proc/self/fd/7"} /dev/fd/7/
                            \return "$(($??$?:$1))"
              else   case   ${a%%=*}      in
                     ([0-9]*|""|*[!_[:alnum:]]*)
                            \printf "%s: \${$i}: Invalid name: %s\n" \
                            >&2    "$0: ns()"   "'\''${a%%=*}'\''"
                            \return 2
              ;;     ("$a") v="$v $a=\$$a"
              ;;     (*)    v="$v ${a%%=*}=\${$i#*=}"
              ;;     esac
                     case " $LOCALS " in (*" ${a%%=*} "*)
              ;;     (*)    LOCALS=$LOCALS" ${a%%=*}"
              ;;     esac
              fi
       done'  7<.    9<>/dev/null
}

Çok zeki! Benzer bir desen burada aynı şeyi elde etmek için kullanılır: github.com/stephane-chazelas/misc-scripts/blob/master/locvar.sh
Zac B
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.