Bash'de noop [:] kullanım durumu nedir?


124

Bash (:) içinde noop aradım, ancak iyi bir bilgi bulamadım. Bu operatörün tam amacı veya kullanım durumu nedir?

Aşağıdakileri denedim ve benim için şu şekilde çalışıyor:

[mandy@root]$ a=11
[mandy@root]$ b=20
[mandy@root]$ c=30
[mandy@root]$ echo $a; : echo $b ; echo $c
10
30

Lütfen bu operatörün herhangi bir kullanım durumunu gerçek zamanlı olarak veya kullanmanın zorunlu olduğu herhangi bir yerde bana bildirin.



:Yerleşik öğenin bourne shell ve ksh ile bash içinde var olduğuna dikkat edin .
ghoti


6
Bu nasıl gerçek bir soru değil? Bence bu gerçekten iyi bir soru. Hatta bunun için iyi bir kullanımım var, ancak bir cevap gönderemiyorum.
Steven Lu

Yanıtlar:


176

Tarihsel nedenlerle daha fazlası var. İki nokta üst üste yerleşimi :tam olarak eşdeğerdir true. trueDönüş değeri önemli olduğunda, örneğin sonsuz döngüde kullanmak gelenekseldir :

while true; do
  echo 'Going on forever'
done

:Kabuk sözdizimi bir komut gerektirdiğinde kullanmak gelenekseldir, ancak yapacak hiçbir şeyiniz yoktur.

while keep_waiting; do
  : # busy-wait
done

:Tüm yerleşik tarihleri yolu geri Thompson kabuğu , öyleydi mevcut yılında Unix v6 . :Thompson kabuğunun gotoaçıklaması için bir etiket göstergesiydi . Etiket herhangi bir metin olabilir, bu nedenle :bir yorum göstergesi olarak ikiye katlanabilir (eğer yoksa goto comment, o : commentzaman etkili bir yorumdur). Bourne kabuğu yoktu gotoama tuttu :.

Yaygın bir deyim kullanır yani :olduğu : ${var=VALUE}, hangi setleri variçin VALUEbu ayar kaldırılamaz ve eğer hiçbir şey yapmazsa varzaten ayarlandı. Bu yapı sadece değişken ikamesi şeklinde mevcuttur ve bu değişken ikamesi bir şekilde bir komutun parçası olmalıdır: işlemsiz bir komut güzelce hizmet eder.

Ayrıca bkz . Kolon inşaası hangi amaca hizmet eder? .


11
Güzel özet. Ayrıca, orijinal Bourne kabuğu #yorumlar (veya #!/bin/shişaretler) için kullanmadı ; :: Yorumcular yıldızlı bir kutu dolusu yapılmış naif programcı betide yorumlar tanıtıldı komuta ve keder : ****(, kötü ya da : * * *).
Jonathan Leffler

3
@JonathanLeffler: Yazıklar olsun bana, ama anlamıyorum. Ne olur : * * *?
DevSolar

7
Çünkü :bir komuttur, kabuk hala olduğunu fark etmeden önce onun argümanları işlemek zorundadır :onları yok sayar. Çoğunlukla, kabuğun *geçerli dizindeki dosyaların bir listesini genişletmek için fazladan iş yapmasını sağlıyorsunuz ; aslında betiğin nasıl çalıştığını etkilemez.
chepner

Sanırım çok sayıda dosya içeren bir dizinde çalıştırırsanız, glob genişlemesinin komut uzunluğu sınırını aşmasına neden olursanız betiğinizin başarısız olmasına neden olabilir? Belki sadece varsa set -e. Her neyse, umarım hepimiz kullanmayı kabul edebiliriz #:)
Jack O'Connor

2
Bazen while : ; do ... ; donehızlı ve kirli bir sonsuz döngü istersem kullanırım . (Genellikle sleepdöngüde bir var ve onu öldürmek için Ctrl-C yazıyorum.)
Keith Thompson

21

Tüm kodu yorumladığımda if ifadeleri için kullanıyorum. Örneğin bir testiniz var:

if [ "$foo" != "1" ]
then
    echo Success
fi

ancak şunun içinde bulunan her şeyi geçici olarak yorumlamak istiyorsunuz:

if [ "$foo" != "1" ]
then
    #echo Success
fi

Bu, bash'nin sözdizimi hatası vermesine neden olur:

line 4: syntax error near unexpected token `fi'
line 4: `fi'

Bash'de boş bloklar (WTF) olamaz. Yani bir işlemsiz eklersiniz:

if [ "$foo" != "1" ]
then
    #echo Success
    :
fi

veya satırları yorumlamak için op no-op'u kullanabilirsiniz:

if [ "$foo" != "1" ]
then
    : echo Success
fi

12

Eğer kullanırsanız set- eo zaman || :için harika bir yoldur değil bir başarısızlık olursa senaryoyu çıkmak (açıkça bu da geçer yapar).


1
Bunu bazı kabuk komut dosyası çağıran uygulamalar için yararlı buldum. Örneğin, Mac'teki Automator hata sonucu çıkacak ve size nedenini söylemeyecektir. Ancak, || :daha sonra hatayı kendiniz ile işlemek, hata exit 0ayıklama sırasında tüm stdout ve stderr'leri uygulama penceresinde görüntülemenizi sağlar. Düşünün { foo ... 2>&1; } || :daha karmaşık tuzakları kurma daha şart-thens daha basit şekilde.
Beejor

7

:Başarılı olan ancak hiçbir şey yapmayan bir komut sağlamak için kullanırsınız . Bu örnekte, "ayrıntı" komutu, varsayılan olarak, ayarı yapılarak kapatılmıştır :. 'V' seçeneği onu açar.

#!/bin/sh
# example
verbosity=:                         
while getopts v OPT ; do          
   case $OPT in                  
       v)        
           verbosity=/bin/realpath 
       ;;
       *)
           exit "Cancelled"
       ;;             
   esac                          
done                              

# `$verbosity` always succeeds by default, but does nothing.                              
for i in * ; do                   
  echo $i $($verbosity $i)         
done                              

$ example
   file

$ example -v
   file /home/me/file  

kesin olarak söylemek gerekirse, bir değişkene bir değer atamak "bir şeyler yapmaktır", bu nedenle case ifadenizde neden bir hata oluşturmaz.
qodeninja

@qodeninja: Hiç kimse değişken atamanın "hiçbir şey yapmadığını" iddia etmedi; mesele şu ki, $(verbosity $i)daha sonra (ve şartlı olarak) hiçbir şey yapmaz.
Orbit'te Hafiflik Yarışları

6

aliasArgümanları yoksay

Bazen herhangi bir tartışmaya gerek duymayan bir takma ada sahip olmak istersiniz. Bunu kullanarak yapabilirsiniz ::

> alias alert_with_args='echo hello there'

> alias alert='echo hello there;:'

> alert_with_args blabla
hello there blabla

> alert blabla
hello there

Olumsuz oy veren değil, ancak bu cevap maalesef nasıl :çalıştığını göstermiyor . Sağladığınız örnek gereksiz görünüyor.
qodeninja

2
Soru nasıl çalıştığı değil , kullanım durumu nedir . Cevabımın stackoverflow.com/a/37170755/6320039'a biraz benzediği doğru . Ama gerçekten aynı kullanım durumu değil. Cevabımı iyileştirmekten memnuniyet duyarım ama burada nasıl olduğunu gerçekten bilmiyorum ..
Ulysse BN

1
Bu biraz garip - bir köşe çantası, isterseniz - ama yine de oldukça ilginç ve kişinin arka cebinde olması akıllıca bir numara olabilir.
Mike S


2
@ZoeyHewll, tam olarak değil. Takma adlar, herhangi bir yürütmeden önce metin olarak değiştirildiği #için, aynı satırdan sonra sözdizimsel olarak gelen her şeyin göz ardı edilmesini sağlamak için bir takma ad kullanmak . Bu gerçekten farklıdır (düşünün my_alias arg ; other_commandveya tersine my_alias arg1 {BACKSLASH}{NEWLINE} arg2) ve muhtemelen istediğiniz şey değildir, çünkü sözdizimi hatalarına neden olabilir (tahmin edin ne yapacak while my_alias ; do true ; doneveya while true ; do my_alias ; donene yapacak).
Maëlan

4

Bir kullanım, çok satırlı yorumlar olarak veya kodunuzun bir bölümünü burada bir dosya ile birlikte kullanarak test amaçlı olarak yorumlamaktır.

: << 'EOF'

This part of the script is a commented out

EOF

EOFİçerideki herhangi bir kodun değerlendirilmemesi için tırnak işaretleri kullanmayı unutmayın $(foo). Aynı zamanda gibi sezgisel sonlandırıcı adını kullanarak değerinde olabilir NOTES, SCRATCHPADya da TODO.


Harika numara! Özellikle sert paketlendiğinde düzinelerce "#" gerektiren kazı kazan notları ve kod için. Bir yan etki, yorum satırlarından farklı olarak, bazı sözdizimi vurgulayıcılarının yorumlu metinleri yok sayması ve onları normal kod (veya en azından düz metin) gibi renklendirmesidir. Bu, yorumlanmış kodu incelemek veya gözlerinizi neon yorum renginde paragraflardan kurtarmak için harika olabilir. Söz dizimi benzersiz olduğundan ve karışıklığa neden olabileceğinden, bu tür blokların paylaşılan kodda yorumlar olarak not edilmesi gerektiğidir. Sonra yine, kafa karışıklığı potansiyelinin sistemik olduğu, bahsettiğimiz kabuktur.
Beejor

3

Benim iki tane.

POD yorumlarını katıştırın

Bir çok korkak bir uygulama :içindir bash komut POD yorumlarınızı gömmeMan sayfalarının hızlı bir şekilde oluşturulabilmesi . Elbette, sonunda tüm komut dosyası Perl ;-) ile yeniden yazılırdı.

Çalışma zamanı işlevi bağlama

Bu, çalışma zamanında bağlama işlevleri için bir tür kod modelidir. Fi, yalnızca belirli bir bayrak ayarlanmışsa bir şeyler yapmak için bir hata ayıklama işlevine sahip olun:

#!/bin/bash
# noop-demo.sh 
shopt -s expand_aliases

dbg=${DBG:-''}

function _log_dbg {
    echo >&2 "[DBG] $@"
}

log_dbg_hook=':'

[ "$dbg" ] && log_dbg_hook='_log_dbg'

alias log_dbg=$log_dbg_hook


echo "Testing noop alias..."
log_dbg 'foo' 'bar'

Sen alırsın:

$ ./noop-demo.sh 
Testing noop alias...
$ DBG=1 ./noop-demo.sh 
Testing noop alias...
[DBG] foo bar

2

Bazen işlemsiz cümlecikler kodunuzu daha okunaklı hale getirebilir.

Bu bir fikir meselesi olabilir, ama işte bir örnek. Diyelim ki iki unix yolu alarak çalışan bir fonksiyon oluşturdunuz. Bir yoldan diğerine cd yapmak için gereken 'değişim yolunu' hesaplar. İşlevinize, yolların her ikisinin de "/" ile başlaması gerektiği VEYA her ikisinin de olmaması gerektiğine dair bir kısıtlama koyarsınız.

function chgpath() {
    # toC, fromC are the first characters of the argument paths.
    if [[ "$toC" == / && "$fromC" == / ]] || [[ "$toC" != / && "$fromC" != / ]]
    then
        true      # continue with function
    else
        return 1  # Skip function.
    fi

Bazı geliştiriciler işlemsizliği kaldırmak isteyecek, ancak bu, koşullu olanı reddetmek anlamına gelir:

function chgpath() {
    # toC, fromC are the first characters of the argument paths.
    if [[ "$toC" != / || "$fromC" == / ]] && [[ "$toC" == / || "$fromC" != / ]]
    then
        return 1  # Skip function.
    fi

Şimdi -bence- if-cümlesinden işlevi yapmayı atlamak isteyeceğiniz koşullar o kadar açık değil. İşlemsizliği ortadan kaldırmak ve bunu açıkça yapmak için, if-cümlesini işlevin dışına taşımak istersiniz:

    if [[ "$toC" == / && "$fromC" == / ]] || [[ "$toC" != / && "$fromC" != / ]]
    then
        cdPath=$(chgPath pathA pathB)   # (we moved the conditional outside)

Bu daha iyi görünüyor, ancak çoğu zaman bunu yapamayız; kontrolün fonksiyon içinde yapılmasını istiyoruz.

Peki bu ne sıklıkla oluyor? Pek sık değil. Belki yılda bir veya iki kez. Yeterince sık oluyor ki bunun farkında olmalısın. Kodumun okunabilirliğini geliştirdiğini düşündüğümde (dilden bağımsız olarak) kullanmaktan çekinmiyorum.


3
Eğer amacı hakkında bir soruyu yanıtlarken ise :, kullanmak gerekir :değil, trueyanıtında,. Yani burada şartlı olumsuzlamak en kolay yolu, bir kullanmaktır dedi [[ ... ]]komuta ve öneki !: if ! [[ ( ... && ... ) || ( ... && ... ) ]]; then.
chepner

1
Ah, çok güzel ve cevabıma oy vermeliyim. Hmmm .... Hâlâ işlem dışı madde kullanmam gereken örnekler düşünüyorum. Bulduğum en iyisi bu.
Bitdiot

Geriye dönük uyumluluk için tutuldu, ancak : ${...=...}tartışmasız daha az garip görünüyor true ${...=...}. Bazıları da kısalığı tercih while :; doedebilir while true; do.
chepner

2

Bir şekilde bu cevapla ilgili olarak, bu işlemsiz , çok dilli komut dosyalarını hacklemek için oldukça uygun buluyorum . Örneğin, burada hem bash hem de vimscript için geçerli bir yorum bulunmaktadır:

":" #    this is a comment
":" #    in bash, ‘:’ is a no-op and ‘#’ starts a comment line
":" #    in vimscript, ‘"’ starts a comment line

Elbette, biz de kullanmış olabiliriz true, ancak :bir noktalama işareti olması ve alakasız bir İngilizce kelime olmaması, bunun bir sözdizimi simgesi olduğunu açıkça ortaya koyuyor.


Birisinin neden çok dilli bir komut dosyası yazmak gibi zor bir şey yapmasına gelince (havalı olmasının yanı sıra): dosyaya Xatıfta bulunarak normalde birkaç farklı dilde birkaç komut dosyası yazdığımız durumlarda yararlıdır Y.

Böyle bir durumda, her iki komut dosyasını da tek bir çok dilli dosyada birleştirmek X, yolun belirlenmesi için herhangi bir çalışmayı önler Y(basitçe "$0"). Daha da önemlisi, programı hareket ettirmeyi veya dağıtmayı daha kolay hale getirir.

  • Yaygın bir örnek. Shebang'larla ilgili iyi bilinen, uzun süredir devam eden bir sorun var: çoğu sistem (Linux ve Cygwin dahil) yorumlayıcıya yalnızca bir argümanın aktarılmasına izin veriyor . Aşağıdaki mesele:

    #!/usr/bin/env interpreter --load-libA --load-libB

    aşağıdaki komutu çalıştıracak:

    /usr/bin/env "interpreter --load-libA --load-libB" "/path/to/script"

    ve amaçlanan değil:

    /usr/bin/env interpreter --load-libA --load-libB "/path/to/script"

    Böylece, aşağıdakiler gibi bir sarmalayıcı komut dosyası yazarsınız:

    #!/usr/bin/env sh
    /usr/bin/env interpreter --load-libA --load-libB "/path/to/script"

    Polyglossia'nın sahneye girdiği yer burasıdır.

  • Daha spesifik bir örnek. Bir keresinde diğer şeylerin yanı sıra Vim'i çağıran bir bash senaryosu yazmıştım. Vim'e seçenekle yapılabilecek ek kurulum vermem gerekiyordu --cmd "arbitrary vimscript command here". Ancak, bu kurulum önemliydi, bu yüzden onu bir dizede satır içine almak korkunç olurdu (eğer mümkünse). Bu nedenle, daha iyi bir çözüm, onu bir yapılandırma dosyasında extenso'da yazmak ve ardından Vim'in bu dosyayı -S "/path/to/file". Bu yüzden bir polyglot bash / vimscript dosyasıyla sonuçlandım.


1

Ayrıca varsayılan değişkenleri tanımlamak için komut dosyalarını kullandım.


: ${VARIABLE1:=my_default_value}
: ${VARIABLE2:=other_default_value}
call-my-script ${VARIABLE1} ${VARIABLE2}

0

bir başkasının başarısına zincirlemek istediğiniz bir komutunuz olduğunu varsayalım:

cmd="some command..."
$cmd
[ $? -eq 0 ] && some-other-command

ancak şimdi komutları koşullu olarak yürütmek istiyorsunuz ve yürütülecek komutları göstermek istiyorsunuz (kuru çalıştırma):

cmd="some command..."
[ ! -z "$DEBUG" ] && echo $cmd
[ -z "$NOEXEC" ] && $cmd
[ $? -eq 0 ] && {
    cmd="some-other-command"
    [ ! -z "$DEBUG" ] && echo $cmd
    [ -z "$NOEXEC" ] && $cmd
}

dolayısıyla DEBUG ve NOEXEC'i ayarlarsanız, ikinci komut asla görünmez. bunun nedeni, ilk komutun hiçbir zaman çalıştırılmamasıdır (çünkü NOEXEC boş değildir), ancak bu gerçeğin değerlendirilmesi sizi 1 dönüşüyle ​​bırakır, bu da alt komutun asla çalıştırılmayacağı anlamına gelir (ancak bunu istersiniz çünkü bir kuru çalışma). bu yüzden bunu düzeltmek için yığında kalan çıkış değerini bir noop ile sıfırlayabilirsiniz:

[ -z "$NOEXEC" ] && $cmd || :
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.