Trường hợp sử dụng của noop [:] trong bash là gì?


124

Tôi đã tìm kiếm noop trong bash (:), nhưng không thể tìm thấy bất kỳ thông tin tốt nào. Mục đích chính xác hoặc trường hợp sử dụng của toán tử này là gì?

Tôi đã thử làm theo và nó hoạt động như thế này đối với tôi:

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

Vui lòng cho tôi biết, bất kỳ trường hợp sử dụng nào của toán tử này trong thời gian thực hoặc bất kỳ nơi nào bắt buộc phải sử dụng nó.



Lưu ý rằng :cài sẵn tồn tại trong bourne shell và ksh cũng như bash.
ghoti 13/09/12


6
Làm thế nào đây không phải là một câu hỏi thực sự? Tôi nghĩ đó là một câu hỏi thực sự hay. Tôi thậm chí có một công dụng tốt cho nó, nhưng tôi không thể đăng câu trả lời.
Steven Lu

Câu trả lời:


176

Nó có nhiều hơn vì lý do lịch sử. Nội trang ruột kết :chính xác tương đương với true. Nó truyền thống để sử dụng truekhi giá trị trả về là quan trọng, ví dụ: trong vòng lặp vô hạn:

while true; do
  echo 'Going on forever'
done

Nó truyền thống để sử dụng :khi cú pháp shell yêu cầu một lệnh nhưng bạn không phải làm gì.

while keep_waiting; do
  : # busy-wait
done

Nội :trang hoàn toàn có từ thời shell Thompson , nó đã có mặt trong Unix v6 . :là một chỉ báo nhãn cho gototuyên bố của Thompson shell . Nhãn có thể là bất kỳ văn bản nào, vì vậy được nhân :đôi lên như một chỉ báo nhận xét (nếu không có goto comment, thì thực sự : commentlà một nhận xét). Các Bourne shell không có gotonhưng vẫn giữ :.

Một thành ngữ phổ biến được sử dụng :: ${var=VALUE}, đặt varthành VALUEnếu nó chưa được đặt và không làm gì nếu varnó đã được đặt. Cấu trúc này chỉ tồn tại ở dạng thay thế biến, và thay thế biến này cần phải là một phần của lệnh bằng cách nào đó: lệnh no-op hoạt động tốt.

Xem thêm Mục đích của nội trang ruột kết là gì? .


11
Tóm tắt tốt. Ngoài ra, vỏ Bourne ban đầu không sử dụng #cho các bình luận (hoặc #!/bin/shshebang); các :lệnh giới thiệu ý kiến, và khốn betide các lập trình viên ngây thơ người đã làm một hộp đẹp của các ngôi sao trong ý kiến của họ: : ****(hoặc, tệ hơn, : * * *).
Jonathan Leffler

3
@JonathanLeffler: Thật xấu hổ cho tôi, nhưng tôi không hiểu. Điều gì sẽ xảy ra : * * *?
DevSolar

7
Bởi vì :là một lệnh, trình bao vẫn phải xử lý các đối số của nó trước khi nó có thể phát hiện ra rằng :bỏ qua chúng. Hầu hết, bạn chỉ đang làm cho trình bao thực hiện thêm công việc trong việc mở rộng *đến danh sách các tệp trong thư mục hiện tại; nó sẽ không thực sự ảnh hưởng đến cách hoạt động của script.
chepner

Tôi cho rằng nó có thể làm cho tập lệnh của bạn bị lỗi nếu bạn tình cờ chạy trong một thư mục có nhiều tệp, làm cho việc mở rộng toàn cầu vượt quá giới hạn độ dài lệnh? Có lẽ chỉ khi bạn có set -e. Dù sao, hy vọng chúng ta có thể tất cả chỉ là đồng ý sử dụng #:)
Jack O'Connor

2
Đôi khi tôi sử dụng while : ; do ... ; donenếu tôi muốn một vòng lặp vô hạn nhanh chóng và bẩn thỉu. (Thường có một sleepvòng lặp trong vòng lặp và tôi gõ Ctrl-C để loại bỏ nó.)
Keith Thompson

21

Tôi sử dụng nó cho câu lệnh if khi tôi nhận xét ra tất cả mã. Ví dụ bạn có một bài kiểm tra:

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

nhưng bạn muốn tạm thời nhận xét mọi thứ có trong:

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

Nguyên nhân khiến bash đưa ra lỗi cú pháp:

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

Bash không được có khối trống (WTF). Vì vậy, bạn thêm một no-op:

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

hoặc bạn có thể sử dụng no-op để nhận xét về các dòng:

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

12

Nếu bạn sử dụng set- ethì đó || :là một cách tuyệt vời để không thoát khỏi tập lệnh nếu xảy ra lỗi (nó rõ ràng làm cho nó vượt qua).


1
Tôi thấy điều này hữu ích cho một số ứng dụng gọi shell-script. Ví dụ: Automator trên Mac sẽ thoát khi bị lỗi và không cho bạn biết lý do. Nhưng việc sử dụng || :sau đó tự xử lý lỗi bằng an exit 0sẽ cho phép bạn hiển thị tất cả stdout và stderr vào cửa sổ ứng dụng trong quá trình gỡ lỗi. Cân nhắc { foo ... 2>&1; } || :cái nào đơn giản hơn nhiều so với việc đặt những cái bẫy phức tạp hơn và nếu có.
Beejor

7

Bạn sẽ sử dụng :để cung cấp một lệnh thành công nhưng không làm gì cả. Trong ví dụ này, lệnh "verbosity" bị tắt theo mặc định, bằng cách đặt nó thành :. Tùy chọn 'v' bật nó lên.

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

nói một cách chính xác, việc gán giá trị cho một biến là "đang làm gì đó", do đó, tại sao nó không tạo ra lỗi trong câu lệnh trường hợp của bạn.
qodeninja

@qodeninja: Không ai tuyên bố rằng việc gán biến "không làm gì cả"; vấn đề là càng về $(verbosity $i)sau (và có điều kiện) không làm gì cả.
Các cuộc đua ánh sáng trong quỹ đạo

6

Bỏ qua các aliasđối số

Đôi khi bạn muốn có một bí danh mà không có bất kỳ đối số nào. Bạn có thể làm điều đó bằng cách sử dụng ::

> alias alert_with_args='echo hello there'

> alias alert='echo hello there;:'

> alert_with_args blabla
hello there blabla

> alert blabla
hello there

không phải là người ủng hộ, nhưng câu trả lời này không chứng minh cách :hoạt động, thật không may. Ví dụ bạn cung cấp có vẻ thừa.
qodeninja

2
Câu hỏi không phải là nó hoạt động như thế nào, mà là trường hợp sử dụng là gì . Đúng là câu trả lời của tôi hơi giống với stackoverflow.com/a/37170755/6320039 . Nhưng nó thực sự không phải là trường hợp sử dụng giống nhau. Tôi muốn được vui để cải thiện câu trả lời của tôi, nhưng tôi thực sự không krnow thế nào đây ..
Ulysse BN

1
Đây là một trường hợp hơi thú vị - một trường hợp góc, nếu bạn muốn - nhưng vẫn khá thú vị và có thể là một mẹo thông minh để có trong túi sau của một người.
Mike S

2
Điều tương tự cũng có thể đạt được với#
Zoey Hewll

2
@ZoeyHewll, không hoàn toàn. Vì bí danh được thay thế bằng văn bản trước bất kỳ loại thực thi nào, việc sử dụng bí danh để #làm cho mọi thứ có cú pháp sau trên cùng một dòng sẽ bị bỏ qua. Điều này thực sự khác biệt (xem xét my_alias arg ; other_commandhoặc ngược lại, my_alias arg1 {BACKSLASH}{NEWLINE} arg2) và có thể không phải là điều bạn muốn vì nó có thể gây ra lỗi cú pháp (đoán xem while my_alias ; do true ; donehoặc while true ; do my_alias ; donesẽ làm gì).
Maëlan

4

Một cách sử dụng là nhận xét nhiều dòng hoặc nhận xét một phần mã của bạn cho mục đích thử nghiệm bằng cách sử dụng nó kết hợp với tệp tại đây.

: << 'EOF'

This part of the script is a commented out

EOF

Đừng quên sử dụng dấu ngoặc kép EOFđể bất kỳ mã nào bên trong không bị đánh giá, chẳng hạn như $(foo). Nó cũng có thể là giá trị sử dụng một tên terminator trực quan như NOTES, SCRATCHPADhoặc TODO.


Mẹo hay! Đặc biệt là đối với các ghi chú cào và mã yêu cầu hàng chục "#" khi được bọc cứng. Một tác dụng phụ là không giống như các dòng bình luận, một số công cụ đánh dấu cú pháp sẽ bỏ qua heredocs, tô màu chúng như mã bình thường (hoặc ít nhất là văn bản thuần túy). Điều này có thể tuyệt vời để kiểm tra mã đã nhận xét hoặc giúp bạn thoát khỏi các đoạn văn bằng màu nhận xét neon. Chỉ lưu ý rằng các khối như vậy nên được lưu ý là nhận xét trong mã được chia sẻ, vì cú pháp là duy nhất và có thể gây nhầm lẫn. Sau đó, một lần nữa, đây là lớp vỏ mà chúng ta đang nói đến, nơi tiềm ẩn sự nhầm lẫn có tính hệ thống.
Beejor

3

Hai của tôi.

Nhúng nhận xét POD

Một ứng dụng khá thú vị :nhúng các bình luận POD vào các tập lệnh bash , để có thể nhanh chóng tạo các trang người dùng. Tất nhiên, cuối cùng người ta sẽ viết lại toàn bộ tập lệnh trong Perl ;-)

Ràng buộc chức năng thời gian chạy

Đây là một loại mẫu mã cho các hàm liên kết tại thời điểm chạy. Fi, có một chức năng gỡ lỗi để thực hiện điều gì đó chỉ khi một cờ nhất định được đặt:

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

Bạn lấy:

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

2

Đôi khi điều khoản no-op có thể làm cho mã của bạn dễ đọc hơn.

Đó có thể là một vấn đề quan điểm, nhưng đây là một ví dụ. Giả sử bạn đã tạo một hàm hoạt động bằng cách sử dụng hai đường dẫn unix. Nó tính toán 'đường dẫn thay đổi' cần thiết để cd từ đường dẫn này sang đường dẫn khác. Bạn đặt ra một hạn chế đối với chức năng của mình rằng cả hai đường dẫn phải bắt đầu bằng '/' HOẶC cả hai đều không được.

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

Một số nhà phát triển sẽ muốn loại bỏ no-op nhưng điều đó có nghĩa là phủ nhận điều kiện:

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

Bây giờ-theo ý kiến ​​của tôi- nó không quá rõ ràng với mệnh đề if-điều kiện mà bạn muốn bỏ qua thực hiện hàm. Để loại bỏ no-op và thực hiện nó một cách rõ ràng, bạn sẽ muốn di chuyển mệnh đề if ra khỏi hàm:

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

Điều đó có vẻ tốt hơn, nhưng nhiều lần chúng tôi không thể làm điều này; chúng ta muốn việc kiểm tra được thực hiện bên trong hàm.

Vậy điều này xảy ra thường xuyên như thế nào? Không thường xuyên lắm. Có thể một hoặc hai lần một năm. Nó xảy ra đủ thường xuyên, mà bạn nên biết về nó. Tôi không ngại sử dụng nó khi tôi nghĩ rằng nó cải thiện khả năng đọc mã của tôi (bất kể ngôn ngữ).


3
Nếu bạn đang trả lời một câu hỏi về mục đích :, bạn nên sử dụng :, không phải true, trong câu trả lời. Điều đó nói rằng, cách dễ nhất để phủ nhận có điều kiện ở đây là sử dụng một [[ ... ]]lệnh và tiền tố nó với !: if ! [[ ( ... && ... ) || ( ... && ... ) ]]; then.
chepner

1
Ah, rất hay, và tôi nên bỏ phiếu cho câu trả lời của mình. Hmmm .... Tôi vẫn đang nghĩ đến những ví dụ mà tôi phải sử dụng điều khoản no-op. Đây là điều tốt nhất tôi nghĩ ra.
Bitdiot

Nó thực sự chỉ được giữ lại để tương thích ngược, mặc dù : ${...=...}được cho là trông ít khó xử hơn true ${...=...}. Một số người cũng có thể thích while :; dosự while true; dongắn gọn.
chepner

2

Hơi liên quan đến câu trả lời này , tôi thấy no-op này khá thuận tiện để hack các tập lệnh đa ngôn ngữ . Ví dụ: đây là một nhận xét hợp lệ cho cả bash và vimscript:

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

Chắc chắn, chúng tôi cũng có thể đã sử dụng true, nhưng :việc là một dấu chấm câu chứ không phải là một từ tiếng Anh không liên quan làm rõ ràng rằng nó là một mã thông báo cú pháp.


Đối với lý do tại sao ai đó lại làm một việc khó khăn như viết một kịch bản đa ngôn ngữ (ngoài việc nó rất hay): nó tỏ ra hữu ích trong các tình huống mà chúng ta thường viết một số tệp kịch bản bằng nhiều ngôn ngữ khác nhau, với tệp Xtham chiếu đến tệp Y.

Trong tình huống như vậy, việc kết hợp cả hai tập lệnh trong một tệp đa ngôn ngữ duy nhất sẽ tránh được bất kỳ công việc nào trong Xviệc xác định đường dẫn đến Y(đơn giản là vậy "$0"). Quan trọng hơn, nó làm cho việc di chuyển hoặc phân phối chương trình trở nên thuận tiện hơn.

  • Một ví dụ phổ biến. Có một vấn đề nổi tiếng lâu đời với shebang: hầu hết các hệ thống (bao gồm cả Linux và Cygwin) chỉ cho phép một đối số được chuyển tới trình thông dịch. Shebang sau:

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

    sẽ kích hoạt lệnh sau:

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

    và không phải mục đích:

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

    Do đó, bạn sẽ phải viết một kịch bản trình bao bọc, chẳng hạn như:

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

    Đây là nơi polyglossia bước vào giai đoạn.

  • Một ví dụ cụ thể hơn. Tôi đã từng viết một kịch bản bash, trong số những thứ khác, sử dụng Vim. Tôi cần cung cấp cho Vim thiết lập bổ sung, có thể được thực hiện với tùy chọn --cmd "arbitrary vimscript command here". Tuy nhiên, thiết lập đó là đáng kể, vì vậy việc nội tuyến nó trong một chuỗi sẽ rất khủng khiếp (nếu có thể). Do đó, một giải pháp tốt hơn là viết nó trong Extenso trong một số tệp cấu hình, sau đó làm cho Vim đọc tệp đó bằng -S "/path/to/file". Do đó, tôi đã kết thúc với một tệp bash / vimscript đa ô.


1

Tôi cũng đã sử dụng các tập lệnh trong đó để xác định các biến mặc định.


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

0

giả sử bạn có một lệnh mà bạn muốn xâu chuỗi thành công của một lệnh khác:

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

nhưng bây giờ bạn muốn thực hiện các lệnh có điều kiện và bạn muốn hiển thị các lệnh sẽ được thực thi (chạy khô):

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

vì vậy nếu bạn đặt DEBUG và NOEXEC, lệnh thứ hai sẽ không bao giờ hiển thị. điều này là do lệnh đầu tiên không bao giờ thực thi (vì NOEXEC không trống) nhưng việc đánh giá thực tế đó để lại cho bạn kết quả là 1, có nghĩa là lệnh cấp dưới không bao giờ thực thi (nhưng bạn muốn nó vì nó chạy khô). vì vậy để khắc phục điều này, bạn có thể đặt lại giá trị thoát còn lại trên ngăn xếp bằng một nút:

[ -z "$NOEXEC" ] && $cmd || :
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.