Apa kasus penggunaan noop [:] di bash?

124

Saya mencari noop di bash (:), tetapi tidak dapat menemukan informasi yang baik. Apa tujuan pasti atau kasus penggunaan operator ini?

Saya mencoba mengikuti dan berfungsi seperti ini untuk saya:

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

Tolong beri tahu saya, kasus penggunaan apa pun dari operator ini secara waktu nyata atau di tempat mana pun yang diwajibkan untuk menggunakannya.

Mandar Pande
sumber
Silakan
Perhatikan bahwa :built-in ada di bourne shell dan ksh serta bash.
ghoti
6
Bagaimana ini bukan pertanyaan nyata? Saya pikir itu pertanyaan yang sangat bagus. Saya bahkan dapat memanfaatkannya dengan baik, tetapi saya tidak dapat memposting jawaban.
Steven Lu

Jawaban:

176

Itu ada lebih karena alasan sejarah. Colon builtin :sama persis dengan true. Ini tradisional untuk digunakan trueketika nilai yang dikembalikan penting, misalnya dalam loop tak terbatas:

while true; do
  echo 'Going on forever'
done

Ini tradisional untuk digunakan :ketika sintaks shell memerlukan perintah tetapi Anda tidak ada yang harus dilakukan.

while keep_waiting; do
  : # busy-wait
done

Tanggal :bawaan semua jalan kembali ke shell Thompson , itu hadir di Unix v6 . :adalah indikator label untuk gotopernyataan shell Thompson . Label bisa berupa teks apa saja, jadi :digandakan sebagai indikator komentar (jika tidak ada goto comment, maka : commentsecara efektif adalah komentar). The Bourne shell tidak memiliki gototapi terus :.

Idiom umum yang digunakan :adalah : ${var=VALUE}, yang disetel varke VALUEjika tidak disetel dan tidak melakukan apa pun jika varsudah disetel. Konstruksi ini hanya ada dalam bentuk substitusi variabel, dan substitusi variabel ini perlu menjadi bagian dari perintah: perintah no-op berfungsi dengan baik.

Lihat juga Apa tujuan usus besar builtin? .

Gilles 'SO- berhenti menjadi jahat'
sumber
11
Ringkasan yang bagus. Juga, shell Bourne asli tidak digunakan #untuk komentar (atau #!/bin/shshebang); yang :perintah diperkenalkan komentar, dan celakalah programmer naif yang membuat bagus kotak bintang di komentar mereka: : ****(atau, lebih buruk lagi, : * * *).
Jonathan Leffler
3
@JonathanLeffler: Aku malu, tapi aku tidak mengerti. Apa yang akan terjadi : * * *?
DevSolar
7
Karena :merupakan sebuah perintah, shell masih harus memproses argumennya sebelum dapat menemukan yang :mengabaikannya. Kebanyakan, Anda hanya membuat shell melakukan pekerjaan ekstra untuk memperluas *daftar file di direktori saat ini; itu tidak akan benar-benar mempengaruhi cara kerja skrip.
chepner
Saya kira itu mungkin membuat skrip Anda gagal jika Anda berjalan di direktori dengan banyak file, membuat perluasan glob melewati batas panjang perintah? Mungkin hanya jika Anda punya set -e. Bagaimanapun, semoga kita semua setuju untuk menggunakan #:)
Jack O'Connor
2
Saya terkadang menggunakan while : ; do ... ; donejika saya ingin loop tak terbatas yang cepat dan kotor. (Biasanya ada sleepdi loop, dan saya mengetik Ctrl-C untuk membunuhnya.)
Keith Thompson
21

Saya menggunakannya untuk pernyataan if ketika saya mengomentari semua kode. Misalnya Anda memiliki tes:

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

tetapi Anda ingin memberi komentar sementara semua yang ada di dalamnya:

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

Yang menyebabkan bash memberikan kesalahan sintaks:

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

Bash tidak boleh memiliki blok kosong (WTF). Jadi Anda menambahkan no-op:

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

atau Anda dapat menggunakan no-op untuk mengomentari baris:

if [ "$foo" != "1" ]
then
    : echo Success
fi
Stephen Ostermiller
sumber
12

Jika Anda menggunakan set- emaka || :adalah cara yang bagus untuk tidak keluar dari skrip jika terjadi kegagalan (secara eksplisit membuatnya lulus).

Chris Pfohl
sumber
1
Saya telah menemukan ini berguna untuk aplikasi pemanggilan shell-script tertentu. Misalnya, Automator di Mac akan keluar saat terjadi kesalahan dan tidak memberi tahu Anda alasannya. Tetapi menggunakan || :kemudian nanti menangani kesalahan sendiri dengan exit 0akan membiarkan Anda menampilkan semua stdout dan stderr ke jendela aplikasi selama debugging. Pertimbangkan { foo ... 2>&1; } || :mana yang lebih sederhana daripada memasang perangkap yang lebih kompleks dan jika-maka.
Beejor
7

Anda akan menggunakan :untuk memberikan perintah yang berhasil tetapi tidak melakukan apa pun. Dalam contoh ini, perintah "verbosity" dimatikan secara default, dengan menyetelnya ke :. Opsi 'v' mengaktifkannya.

#!/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  
Menandai
sumber
tegasnya, menetapkan nilai ke variabel adalah "melakukan sesuatu", oleh karena itu mengapa tidak menimbulkan kesalahan dalam pernyataan kasus Anda.
qodeninja
@qodeninja: Tidak ada yang mengklaim bahwa tugas variabel "tidak melakukan apa-apa"; intinya adalah bahwa $(verbosity $i)nanti (dan secara kondisional) tidak melakukan apa pun.
Balapan Ringan di Orbit
6

Abaikan aliasargumen

Terkadang Anda ingin memiliki alias yang tidak membutuhkan argumen apa pun. Anda dapat melakukannya dengan menggunakan ::

> alias alert_with_args='echo hello there'

> alias alert='echo hello there;:'

> alert_with_args blabla
hello there blabla

> alert blabla
hello there
Ulysse BN
sumber
bukan downvoter, tapi sayangnya jawaban ini tidak menunjukkan cara :kerjanya. Contoh yang Anda berikan tampaknya berlebihan.
qodeninja
2
Pertanyaannya bukanlah bagaimana cara kerjanya, tapi apa kasus penggunaannya . Memang benar jawaban saya agak mirip dengan stackoverflow.com/a/37170755/6320039 . Tapi ini benar-benar bukan kasus penggunaan yang sama. Saya akan dengan senang hati meningkatkan jawaban saya tetapi saya benar-benar tidak tahu caranya di sini ..
Ulysse BN
1
Ini agak funky- case sudut, jika Anda mau- tapi masih cukup menarik dan bisa menjadi trik cerdas untuk disimpan di saku belakang.
Mike S
2
Hal yang sama dapat dicapai dengan#
Zoey Hewll
2
@ZoeyHewll, kurang tepat. Karena alias diganti secara tekstual sebelum segala jenis eksekusi, menggunakan alias to #akan membuat semua yang muncul secara sintaksis setelah pada baris yang sama diabaikan. Ini sangat berbeda (pertimbangkan my_alias arg ; other_commandatau, sebaliknya, my_alias arg1 {BACKSLASH}{NEWLINE} arg2), dan mungkin bukan yang Anda inginkan karena dapat menyebabkan kesalahan sintaks (coba tebak while my_alias ; do true ; doneatau yang while true ; do my_alias ; doneakan dilakukan).
Maëlan
4

Satu kegunaannya adalah sebagai komentar multiline, atau untuk mengomentari sebagian kode Anda untuk tujuan pengujian dengan menggunakannya bersama dengan file di sini.

: << 'EOF'

This part of the script is a commented out

EOF

Jangan lupa untuk menggunakan tanda kutip EOFagar kode apa pun di dalamnya tidak dapat dievaluasi, seperti $(foo). Hal ini juga mungkin layak menggunakan nama terminator intuitif seperti NOTES, SCRATCHPAD, atau TODO.

Geoffrey Ritchey
sumber
Trik keren! Terutama untuk catatan awal dan kode yang membutuhkan lusinan "#" saat dibungkus dengan keras. Efek sampingnya adalah tidak seperti baris komentar, beberapa penyorot sintaks akan mengabaikan heredocs, mewarnainya seperti kode normal (atau setidaknya teks biasa). Yang bisa bagus untuk memeriksa kode komentar, atau menyelamatkan mata Anda dari paragraf dengan warna komentar neon. Satu-satunya peringatan adalah bahwa blok semacam itu harus dicatat sebagai komentar dalam kode bersama, karena sintaksnya unik dan dapat menyebabkan kebingungan. Kemudian lagi, ini adalah shell yang sedang kita bicarakan, di mana potensi kebingungan bersifat sistemik.
Beejor
3

Dua milikku.

Sematkan komentar POD

Aplikasi yang cukup funky :adalah untuk menanamkan komentar POD dalam skrip bash , sehingga halaman manual dapat dibuat dengan cepat. Tentu saja, seseorang pada akhirnya akan menulis ulang seluruh skrip di Perl ;-)

Pengikatan fungsi run-time

Ini adalah semacam pola kode untuk fungsi pengikatan pada waktu proses. Fi, miliki fungsi debugging untuk melakukan sesuatu hanya jika flag tertentu disetel:

#!/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'

Anda mendapatkan:

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

Terkadang klausa tanpa operasi dapat membuat kode Anda lebih mudah dibaca.

Itu bisa jadi masalah opini, tapi inilah contohnya. Misalkan Anda telah membuat fungsi yang bekerja dengan mengambil dua jalur unix. Ini menghitung 'jalur perubahan' yang diperlukan untuk cd dari satu jalur ke jalur lainnya. Anda menempatkan batasan pada fungsi Anda bahwa jalur harus dimulai dengan '/' ATAU keduanya tidak boleh.

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

Beberapa pengembang ingin menghapus no-op tetapi itu berarti meniadakan kondisional:

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

Sekarang-menurut pendapat saya- tidak begitu jelas dari klausa if kondisi di mana Anda ingin melewatkan melakukan fungsi. Untuk menghilangkan no-op dan melakukannya dengan jelas, Anda perlu memindahkan klausa if dari fungsi:

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

Kelihatannya lebih baik, tetapi seringkali kita tidak dapat melakukan ini; kami ingin pemeriksaan dilakukan di dalam fungsi.

Jadi seberapa sering ini terjadi? Tidak terlalu sering. Mungkin sekali atau dua kali setahun. Itu cukup sering terjadi, sehingga Anda harus menyadarinya. Saya tidak segan-segan menggunakannya ketika menurut saya itu meningkatkan keterbacaan kode saya (terlepas dari bahasanya).

Bitdiot
sumber
3
Jika Anda menjawab pertanyaan tentang tujuan :, Anda harus menggunakan :, bukan true, dalam jawaban. Yang mengatakan, cara termudah untuk meniadakan bersyarat sini adalah dengan menggunakan salah satu [[ ... ]]perintah dan awalan dengan !: if ! [[ ( ... && ... ) || ( ... && ... ) ]]; then.
chepner
1
Ah, sangat bagus, dan saya harus memilih jawaban saya. Hmmm .... Saya masih memikirkan contoh di mana saya harus menggunakan klausa no-op. Ini yang terbaik yang saya dapatkan.
Bitdiot
Ini benar-benar hanya dipertahankan untuk kompatibilitas ke belakang, meskipun : ${...=...}bisa dibilang kurang terlihat canggung daripada true ${...=...}. Beberapa mungkin lebih memilih while :; dountuk while true; dountuk terseness juga.
chepner
2

Agak terkait dengan jawaban ini , menurut saya no-op ini agak nyaman untuk meretas skrip poliglot . Misalnya, berikut adalah komentar yang valid untuk bash dan untuk vimscript:

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

Tentu, kami mungkin juga menggunakannya true, tetapi :menjadi tanda baca dan bukan kata bahasa Inggris yang tidak relevan memperjelas bahwa ini adalah token sintaks.


Adapun mengapa seseorang melakukan hal yang rumit seperti menulis skrip poliglot (selain itu keren): ini terbukti membantu dalam situasi di mana kami biasanya menulis beberapa file skrip dalam beberapa bahasa berbeda, dengan file yang Xmerujuk ke file Y.

Dalam situasi seperti itu, menggabungkan kedua skrip dalam satu file polyglot menghindari pekerjaan apa pun Xuntuk menentukan jalur ke Y(itu sederhana "$0"). Lebih penting lagi, ini membuatnya lebih nyaman untuk bergerak atau mendistribusikan program.

  • Contoh umum. Ada masalah lama yang terkenal dengan shebang: sebagian besar sistem (termasuk Linux dan Cygwin) hanya mengizinkan satu argumen untuk diteruskan ke penerjemah. Shebang berikut:

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

    akan mengaktifkan perintah berikut:

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

    dan bukan yang dimaksudkan:

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

    Dengan demikian, Anda akan menulis skrip pembungkus, seperti:

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

    Di sinilah poliglosia memasuki panggung.

  • Contoh yang lebih spesifik. Saya pernah menulis skrip bash yang, antara lain, menggunakan Vim. Saya perlu memberi Vim pengaturan tambahan, yang dapat dilakukan dengan opsi --cmd "arbitrary vimscript command here". Namun, penyiapan itu substansial, sehingga membuat sebaris dalam string akan sangat buruk (jika memungkinkan). Oleh karena itu, solusi yang lebih baik adalah menuliskannya dalam extenso di beberapa file konfigurasi, kemudian membuat Vim membaca file itu dengan -S "/path/to/file". Karenanya saya berakhir dengan file polyglot bash / vimscript.

Maëlan
sumber
1

Saya juga menggunakan skrip di dalamnya untuk menentukan variabel default.


: ${VARIABLE1:=my_default_value}
: ${VARIABLE2:=other_default_value}
call-my-script ${VARIABLE1} ${VARIABLE2}
Brendan Abel
sumber
0

misalkan Anda memiliki perintah yang ingin Anda rangkai untuk kesuksesan yang lain:

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

tetapi sekarang Anda ingin menjalankan perintah secara kondisional dan Anda ingin menampilkan perintah yang akan dijalankan (dry-run):

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

jadi jika Anda menyetel DEBUG dan NOEXEC, perintah kedua tidak akan pernah muncul. ini karena perintah pertama tidak pernah dijalankan (karena NOEXEC tidak kosong) tetapi evaluasi fakta tersebut membuat Anda mendapatkan pengembalian 1, yang berarti perintah subordinat tidak pernah dijalankan (tetapi Anda menginginkannya karena ini adalah proses kering). jadi untuk memperbaikinya, Anda dapat mengatur ulang nilai keluar yang tersisa di tumpukan dengan noop:

[ -z "$NOEXEC" ] && $cmd || :
ekkis
sumber