(Kolon) GNU Bash yerleşiminin amacı nedir?


336

Hiçbir şey yapmayan, yorum liderinden biraz daha fazlası olan, ama aslında kendi içinde bir kabuk yerleşik olan bir emrin amacı nedir?

Komut dosyalarınıza arama başına yaklaşık% 40 oranında bir yorum eklemekten daha yavaştır; bu, muhtemelen yorumun boyutuna bağlı olarak büyük ölçüde değişir. Bunun için görebildiğim tek neden:

# poor man's delay function
for ((x=0;x<100000;++x)) ; do : ; done

# inserting comments into string of commands
command ; command ; : we need a comment in here for some reason ; command

# an alias for `true' (lazy programming)
while : ; do command ; done

Gerçekten ne aradığımı tahmin ediyorum ne tarihsel bir uygulama olmuş olabilir.



69
@Caleb - Bunu bundan iki yıl önce sordum.
amfetamin

Belirli bir değeri döndüren bir komut söyleyemem "hiçbir şey yapmaz." İşlevsel programlama "hiçbir şey yapmamaktan" oluşmazsa. :-)
LarsH

Yanıtlar:


415

Tarihsel olarak , Bourne kabukları yoktu trueve falsegibi yerleşik komutlar. truebunun yerine basitçe :ve falsebenzeri bir şeye takma ad verildi let 0.

:trueBourne'den türetilmiş mermilere taşınabilirlikten biraz daha iyidir . Basit bir örnek olarak, ne !boru hattı operatörüne ne de ||liste operatörüne sahip olmayı düşünün (bazı eski Bourne mermilerinde olduğu gibi). Bu else, ififadenin cümlesini çıkış durumuna göre dallanma için tek araç olarak bırakır :

if command; then :; else ...; fi

Yana ifboş olmayan gerektirir thenmaddesini ve yorumlar boş olmayan olarak sayılmaz, :no-op olarak hizmet vermektedir.

Günümüzde (yani: modern bir bağlamda) genellikle ya :da kullanabilirsiniz true. Her ikisi de POSIX tarafından belirtilir ve bazıları truedaha kolay okunur. : Ancak orada bir ilginç farktır :sözde POSIX yerleşik özel oysa truebir olduğunu yerleşik düzenli .

  • Kabuğun içine özel ankastre yapıların yerleştirilmesi gerekir; Düzenli yerleşikler yalnızca "tipik olarak" yerleşiktir, ancak kesinlikle garanti edilmez. Genellikle çoğu sistemin PATH :işleviyle adlandırılan düzenli bir program olmamalıdır true.

  • Muhtemelen en önemli fark, özel yerleşikler ile, yerleşik tarafından ayarlanan herhangi bir değişkenin - basit komut değerlendirmesi sırasında çevrede bile - burada ksh93 kullanılarak gösterildiği gibi, komut tamamlandıktan sonra da devam etmesidir:

    $ unset x; ( x=hi :; echo "$x" )
    hi
    $ ( x=hi true; echo "$x" )
    
    $
    

    Zsh'ın, POSIX uyumluluk modunda çalışma dışında GNU Bash gibi bu gereksinimi göz ardı ettiğini, ancak diğer tüm "POSIX sh türetilmiş" mermilerin çizgi, ksh93 ve mksh dahil bunu gözlemlediğini unutmayın.

  • Başka bir fark, normal yerleşiklerin uyumlu olması gerektiğidir exec- burada Bash kullanılarak gösterilmiştir:

    $ ( exec : )
    -bash: exec: :: not found
    $ ( exec true )
    $
    
  • POSIX , elbette uygulamaya özel bir ayrıntı olsa da, açıkça :daha hızlı olabileceğini de belirtiyor true.


Düzenli yerleşik ins gerekir kastettiniz değil uyumlu olacak exec?
Old Pro

1
@OldPro: Hayır, bu truenormal bir yerleşik olduğu için doğrudur , ancak yerleşik yerine execkullanmakta yanlıştır /bin/true.
sonraki duyuruya kadar duraklatıldı.

1
@DennisWilliamson Sadece spec ifade bu arada gidiyordum. Bunun anlamı, normal yapıların bağımsız bir versiyonunun da mevcut olması gerektiğidir.
ormaaj

17
+1 Mükemmel cevap. Yine de, değişkenleri başlatmak için, : ${var?not initialized}et al.
Üçlü

Az çok ilgisiz takip. :Özel bir yerleşik olduğunu söylediniz ve adında bir işlev olmamalıdır. Ancak çatal bomba :(){ :|: & };:isimlendirme işlevine en sık rastlanan örnek değil :mi?
Chong

63

Değişken komutlarını kolayca etkinleştirmek / devre dışı bırakmak için kullanıyorum:

#!/bin/bash
if [[ "$VERBOSE" == "" || "$VERBOSE" == "0" ]]; then
    vecho=":"     # no "verbose echo"
else
    vecho=echo    # enable "verbose echo"
fi

$vecho "Verbose echo is ON"

Böylece

$ ./vecho
$ VERBOSE=1 ./vecho
Verbose echo is ON

Bu temiz bir komut dosyası sağlar. Bu '#' ile yapılamaz.

Ayrıca,

: >afile

'afile' varlığını garanti etmenin en basit yollarından biridir ancak 0 uzunluktur.


20
>afiledaha da basittir ve aynı etkiye sahiptir.
erken

2
Güzel, koruduğum scriptleri basitleştirmek için bu $ vecho hilesini kullanacağım.
BarneySchmale

5
İki nokta üst üste alıntı yapmanın faydası nedir vecho=":"? Sadece okunabilirlik için mi?
leoj

56

Yararlı bir uygulama: sonuçları bir komuta iletmek yerine sadece yan etkileri için parametre genişletmeleri kullanmakla ilgileniyorsanız. Bu durumda, PE'yi 0 veya 1 çıkış durumu isteyip istemediğinize bağlı olarak: veya false değişkenine bağımsız değişken olarak kullanırsınız : "${var:=$1}". Yana :bir yerleşiğidir oldukça hızlı olmalıdır.


2
Aritmetik genişlemenin yan etkileri için de kullanabilirsiniz: : $((a += 1))( ++ve --operatörlerin POSIX'e göre uygulanması gerekmez.). Bash, ksh ve diğer olası mermilerde ayrıca şunları yapabilirsiniz: ((a += 1))ya da ((a++))POSIX tarafından belirtilmemiştir.
pabouk

@pabouk Evet, hepsi (())isteğe bağlıdır , ancak isteğe bağlı bir özellik olarak belirtilmiştir. "" (("İle başlayan bir karakter dizisi, kabuktan önce '$' gelirse aritmetik genişletme olarak ayrıştırılırsa," ((ifade)) "ifadesinin aritmetik bir ifade olarak değerlendirildiği bir uzantı uygulayan kabuklar "((", bir gruplama komutu yerine aritmetik bir değerlendirme olarak tanıtıyor. ")
ormaaj

50

:ayrıca blok yorumu için de olabilir (C dilinde / * * / biçimine benzer). Örneğin, komut dosyanızdaki bir kod bloğunu atlamak istiyorsanız, bunu yapabilirsiniz:

: << 'SKIP'

your code block here

SKIP

3
Kötü bir fikir. Bu belgedeki komut ikameleri hala işlenmektedir.
chepner

33
O kadar da kötü bir fikir değil. Sınırlayıcıyı tek tırnak içine alarak bu dokümanlardaki değişken çözünürlük / ikame işleminden kaçınabilirsiniz: << << SKIP '
Rondo

1
IIRC da olabilir \ aynı etki için sınırlayıcı karakterlerden herhangi kaçış: : <<\SKIP.
yyny

@zagpoint Python'un docstrings'i çok satırlı yorumlar olarak kullanacağı yer burası mı?
Sapphire_Brick

31

Günlükleri temizlemek için bir dosyayı sıfır bayta kesmek isterseniz, şunu deneyin:

:> file.log

16
> file.logdaha basittir ve aynı etkiye sahiptir.
amfetamachine

55
Yah, ama mutlu yüz benim için ne yapıyor:>
Ahi Tuna

23
@amphetamachine: :>daha taşınabilir. Bazı kabuklar (benim gibi zsh), mevcut kabukta bir kediyi otomatik olarak başlatır ve komut olmadan bir yönlendirme verildiğinde stdin'i dinler. Yerine cat /dev/null, :çok daha kolaydır. Genellikle bu davranış, komut dosyalarından ziyade etkileşimli kabuklarda farklıdır, ancak komut dosyasını etkileşimli olarak da çalışacak şekilde yazarsanız, kopyala-yapıştır ile hata ayıklama çok daha kolaydır.
Caleb

2
Nasıl gelmez : > filefarklılık true > filemodern kabukta (karakter sayısı ve mutlu yüzünden kenara) (varsayarak :ve trueeşit hızlı)?
Adam Katz


29

Diğer cevaplarda belirtilmeyen iki kullanım daha:

Kerestecilik

Bu örnek komut dosyasını alın:

set -x
: Logging message here
example_command

İlk satır, set -xkabuğun çalıştırmadan önce komutu yazdırmasını sağlar. Oldukça kullanışlı bir yapı. Dezavantajı, normal ifade echo Log messagetürünün mesajı iki kez yazdırmasıdır. İki nokta üst üste yöntemi bunu tamamlar. Yine de tıpkı sizin gibi özel karakterlerden kaçmanız gerektiğini unutmayın echo.

Cron iş unvanları

Bunun gibi cron işlerinde kullanıldığını gördüm:

45 10 * * * : Backup for database ; /opt/backup.sh

Bu, senaryoyu /opt/backup.shher gün 10:45'te çalıştıran bir cron işi . Bu tekniğin avantajı, /opt/backup.shbazı çıktılar yazdırıldığında daha iyi görünümlü e-posta konuları oluşturmasıdır .


Varsayılan günlük konumu nerede? Günlük konumunu ayarlayabilir miyim? Amaç daha çok komut dosyaları / arka plan işlemleri sırasında stdout'ta çıktı oluşturmak mı?
domdambrogia

1
@domdambrogia Kullanırken set -x, yazdırılan komutlar (gibi bir şey dahil : foobar) stderr'e gider.
Flimm

23

``Bir komutu çıktısını görüntülemeden yürütmek için backticks ( ) ile birlikte kullanabilirsiniz , örneğin:

: `some_command`

Tabii ki yapabilirsin some_command > /dev/null, ama :-versiyon biraz daha kısa.

Sadece insanları karıştırdığı için bunu yapmayı tavsiye etmeyeceğim söyleniyor. Sadece olası bir kullanım örneği olarak akla geldi.


25
Komut birkaç megabaytlık çıktıyı dökecekse bu güvenli değildir, çünkü kabuk çıktıyı arabelleğe alır ve komut satırı bağımsız değişkenleri (yığın alanı) olarak ':' olarak iletir.
Juliano

15

Polyglot programları için de yararlıdır:

#!/usr/bin/env sh
':' //; exec "$(command -v node)" "$0" "$@"
~function(){ ... }

Bu artık bir yürütülebilir kabuk kod hem de ve anlamı: bir JavaScript programı ./filename.js, sh filename.jsve node filename.jstüm çalışma.

(Kesinlikle biraz garip bir kullanım, ancak yine de etkili.)


İstendiği gibi bazı açıklamalar:

  • Kabuk betikleri satır satır değerlendirilir; ve execkomut çalıştırmak, kabuk ve sona erer yerine geçer elde edilen komutu ile süreç. Bu, kabuk için programın şöyle görüneceği anlamına gelir:

    #!/usr/bin/env sh
    ':' //; exec "$(command -v node)" "$0" "$@"
  • Kelimede herhangi bir parametre genişlemesi veya diğer adı oluşmadığı sürece , bir kabuk komut dosyasındaki herhangi bir sözcük, anlamını değiştirmeden tırnak işaretleri içine alınabilir; bu ':', eşdeğer olduğu anlamına gelir :(aşağıda açıklanan JavaScript anlambilimine ulaşmak için buraya yalnızca tırnak işaretleri içine aldık)

  • ... ve yukarıda açıklandığı gibi, ilk satırdaki ilk komut bir no-op'dur ( : //kelimeleri ifade eder veya alıntılamayı tercih ederseniz, burada) JavaScript'te olduğu gibi burada özel bir anlam taşımadığına ':' '//'dikkat edin //; sadece atılan anlamsız bir kelime.)

  • Son olarak, ilk satırdaki (noktalı virgülden sonra) ikinci komut, programın gerçek etidir: komut dosyasının geri kalanını değerlendirmek için çağrılan bir Node.js işlemi ile çağrılan kabuk komut dosyasınınexec yerini alan çağrıdır .

  • Bu arada, JavaScript'teki ilk satır, bir string-literal ( ':') ve ardından silinmiş bir yorum olarak ayrıştırılır ; böylece, JavaScript'e göre, program şöyle görünür:

    ':'
    ~function(){ ... }

    String-literal kendi başına bir çizgi üzerinde olduğundan, no-op ifadesidir ve bu nedenle programdan çıkarılır; yani tüm satır kaldırılır, yalnızca program kodunuz kalır (bu örnekte function(){ ... }gövde).


Merhaba, ne açıklayabilir : //;ve ~function(){}do? Teşekkür ederim:)
Stphane

1
@Stphane Bir arıza eklendi! Gelince ~function(){}, bu biraz daha karmaşık. Orada bir çift bu soruların ne siz, burada bir soru olarak göndermek için çekinmeyin için de yeterince açıklıyor eğer gerçekten bunların hiçbiri ... benim memnuniyeti için bunu açıklamak rağmen üzerinde dokunma, yani ben olacağım Burada diğer cevaplar yeni bir soruya derinlemesine cevap vermekten mutluluk duyarız.
ELLIOTTCABLE

1
Ben dikkat etmedim node. Yani fonksiyon kısmı tamamen javascript ile ilgilidir! IIFE'nin önünde tek operatör ile iyiyim. Ben bu ilk etapta bash olduğunu düşündüm ve aslında gerçekten yazı anlamını alamadım. Şimdi iyiyim, «ayırma» ekleyerek geçirdiğiniz zaman için teşekkür ederiz!
Stphane

~{ No problem. (= }
ELLIOTTCABLE

12

Kendini belgeleme işlevleri

:Belgeleri bir işleve gömmek için de kullanabilirsiniz .

mylib.shÇeşitli işlevler sağlayan bir kütüphane komut dosyanız olduğunu varsayalım . Kitaplığı ( . mylib.sh) kaynaklayabilir ve hemen ardından ( lib_function1 arg1 arg2) işlevleri çağırabilir veya ad alanınızı karıştırmaktan kaçınarak kitaplığı bir işlev bağımsız değişkeniyle ( mylib.sh lib_function1 arg1 arg2) çağırabilirsiniz .

mylib.sh --helpYardım metnindeki işlev listesini el ile tutmak zorunda kalmadan, kullanılabilir işlevlerin ve bunların kullanımlarının bir listesini yazıp alabilmeniz hoş olmaz mıydı ?

#! / Bin / bash

# tüm "genel" işlevler bu önekle başlamalıdır
LIB_PREFIX 'lib_' =

# "genel" kütüphane işlevi
lib_function1 () {
    : Bu işlev iki argümanla karmaşık bir şey yapar.
    :
    : Parametreler:
    : 'arg1 - ilk argüman (1 $)'
    : 'arg2 - ikinci argüman'
    :
    : Sonuç:
    : " karmaşık"

    # gerçek fonksiyon kodu burada başlar
}

lib_function2 () {
    : İşlev belgeleri

    # fonksiyon kodu buraya
}

# yardım işlevi
--Yardım() {
    yankı MyLib v0.0.1
    Eko
    echo Kullanım: mylib.sh [işlev_adı [args]]
    Eko
    echo Kullanılabilir işlevler:
    beyan -f | sed -n -e '/ ^' $ LIB_PREFIX '/, / ^} $ / {/ \ (^' $ LIB_PREFIX '\) \ | \ (^ [\ t] *: \) / {
        s / ^ \ ('$ LIB_PREFIX'. * \) () / \ n === \ 1 === /; s / ^ [\ t] *: \? ['\' '"] \? / / s / [ '\' '"] \;? \ $ //; * p}}'
}

# ana kod
eğer ["$ {BASH_SOURCE [0]}" = "$ {0}"]; sonra
    # kod kaynak yerine çalıştırıldı
    # istenen işlevi çağır veya yardımı göster
    eğer ["$ (tip -t -" $ 1 "2> / dev / null)" = işlev]; sonra
        "$ @"
    Başka
        --Yardım
    fi
fi

Kod hakkında birkaç yorum:

  1. Tüm "genel" işlevler aynı ön eke sahiptir. Yalnızca bunlar kullanıcı tarafından çağrılmalı ve yardım metninde listelenmelidir.
  2. Kendi kendini belgeleme özelliği bir önceki noktaya dayanır ve kullanılabilir declare -ftüm işlevleri numaralandırmak için kullanır , daha sonra yalnızca uygun önekle işlevleri görüntülemek için bunları sed üzerinden filtreler.
  3. İstenmeyen genişleme ve boşlukların kaldırılmasını önlemek için belgeleri tek tırnak içine almak iyi bir fikirdir. Metinde kesme işaretlerini / tırnak işaretlerini kullanırken de dikkatli olmanız gerekir.
  4. Kütüphane önekini içselleştirmek için kod yazabilirsiniz, yani kullanıcı sadece yazmak zorundadır mylib.sh function1ve dahili olarak çevrilir lib_function1. Bu okuyucuya bırakılan bir alıştırmadır.
  5. Yardım işlevi "--help" olarak adlandırılır. Bu, ek bir kontrolü kodlamak zorunda kalmadan yardımın kendisini görüntülemek için kütüphane çağırma mekanizmasını kullanan kullanışlı (yani tembel) bir yaklaşımdır $1. Aynı zamanda, kitaplığı kaynak yaparsanız ad alanınızı daraltır. Bunu beğenmezseniz, adı benzer bir adla değiştirebilir lib_helpveya --helpana koddaki bağımsız değişkenleri kontrol edebilir ve yardım işlevini manuel olarak çağırabilirsiniz.

4

Bir komut dosyasında bu kullanımı gördüm ve bir komut dosyası içinde basename çağırmak için iyi bir yedek olduğunu düşündüm.

oldIFS=$IFS  
IFS=/  
for basetool in $0 ; do : ; done  
IFS=$oldIFS  

... bu kodun yerine geçer: basetool=$(basename $0)


Tercih ederimbasetool=${0##*/}
Amit Naidu
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.