Tiện ích của lệnh: trong kịch bản lệnh shell, cho rằng nó rõ ràng không làm gì?


27

Trong câu trả lời cho câu hỏi này về các bình luận trong kịch bản shell , nó được chỉ ra rằng đó :là một lệnh null hoàn toàn không làm gì cả (nhưng không được sử dụng cho các bình luận).

Điều gì sẽ là tiện ích của một lệnh hoàn toàn không có gì?



2
Xem thêm câu hỏi này có câu trả lời thậm chí tốt hơn ở đây , cụ thể :là bắt buộc phải có sẵn, trong khi truekhông, điều này ảnh hưởng đến phạm vi của các biến
Old Pro

2
Nó rõ ràng không có gì duyên dáng .
mikeerv

Câu trả lời:


19

Tôi thường sử dụng truetrong các vòng lặp; Tôi nghĩ nó rõ ràng hơn:

while true; do
    ...
done

Một nơi tôi thấy :thực sự tiện dụng là trong các báo cáo trường hợp, nếu bạn cần khớp một thứ gì đó nhưng không thực sự muốn làm gì. Ví dụ:

case $answer in
    ([Yy]*) : ok ;;
    (*)     echo "stop."; exit 1 ;;
esac

3
Tôi đồng ý. truecho một điều kiện, :cho một NOP.
jw013

1
bash chấp nhận case a in a ) ;; esac. Có một số vỏ không chấp nhận điều này?
Kaz

@Kaz: Theo ngữ pháp shell POSIX , case ${var} in value);; *) do_something;; esaccó thể chấp nhận được. Các :lệnh là không cần thiết đối với trường hợp có sản phẩm nào.
Richard Hansen

12

Ban đầu, nó được sử dụng để xác định rằng đó là chương trình shell Bourne, trái ngược với chương trình biên dịch C. Điều này là trước khi shebang và nhiều ngôn ngữ kịch bản (csh, perl). Bạn vẫn có thể chạy tập lệnh bắt đầu chỉ bằng ::

$ echo : > /tmp/xyzzy
$ chmod +x /tmp/xyzzy
$ ./xyzzy

Nó thường sẽ chạy tập lệnh chống lại $SHELL(hoặc /bin/sh).

Kể từ đó, việc sử dụng chính là để đánh giá các đối số. Tôi vẫn sử dụng:

: ${EDITOR:=vim}

để đặt giá trị mặc định trong tập lệnh.


11

: là hữu ích để viết các vòng lặp phải được chấm dứt từ bên trong.

while :
do
    ...stuff...
done

Điều này sẽ chạy mãi mãi trừ khi breakhoặc exitđược gọi, hoặc shell nhận được tín hiệu kết thúc.


3
Tôi cảm thấy rằng while true; do ...; donetruyền đạt ý định đến người đọc tốt hơnwhile :; do ...; done
Richard Hansen

9

Khi bạn muốn một câu lệnh "trừ khi" trong kịch bản shell, bạn có thể sử dụng điều kiện "không", điều này có thể trông ngớ ngẩn đối với một số thử nghiệm hoặc bạn sử dụng ':' trong mệnh đề đúng, với mã thực trong sai- mệnh đề.

if [ some-exotic-condition ]
then
    :
else
    # Real code here
fi

"Điều kiện kỳ ​​lạ" có thể là điều bạn không muốn phủ nhận hoặc điều đó rõ ràng hơn nhiều nếu bạn không sử dụng "logic tiêu cực".


1
Nó cũng hiển thị trong các tập lệnh được tạo bởi autoconfvì việc thêm mặc định :cho các nhánh trống dễ dàng hơn nhiều so với việc tìm ra cách đảo ngược điều kiện.
Dietrich Epp

2
Tôi không thể thấy việc gắn bó !trước mặt [ some-exotic-condition ]là ngớ ngẩn, nhưng thừa thãi : elsesau khi nó không ngớ ngẩn.
Kaz

@Kaz có một điểm tốt. Hãy nhớ rằng đến với các điều kiện kỳ ​​lạ là khó khăn nhất. Nếu bạn phải phủ nhận tất cả, đó là một điều, nhưng nó có thể chỉ làm cho tình trạng không rõ ràng. Có một '!' phủ nhận toàn bộ điều kiện, hay chỉ là thuật ngữ đầu tiên? Tốt nhất để có một mệnh đề đúng ':' đôi khi.
Bruce Ediger

!thông báo phủ nhận toàn bộ thành phần ống lệnh. while ! grep ... ; do ... done hoặc if ! [ ... ] ; then ... fi. Về cơ bản, nó nằm ngoài test/[]cú pháp. Xem: pubs.opengroup.org/onlinepub/9699919799/utilities/iêu
Kaz

5

Tôi chỉ từng sử dụng điều này ngoài ký tự # để tạm thời nhận ra một dòng, trong tình huống nhận xét dòng tạo ra lỗi cú pháp, do lỗi ngữ pháp shell không cho phép một chuỗi lệnh trống :

if condition ; then
    :# temporarily commented out command
fi

Nếu không có: chúng ta có một chuỗi lệnh bị thiếu, đó là lỗi cú pháp.


4

Có hai trường hợp tôi thấy :hữu ích:

Bài tập biến mặc định

#!/bin/sh

# set VAR to "default value" if not already set in the environment
: "${VAR=default value}"

# print the value of the VAR variable.  Note that POSIX says the behavior
# of echo is implementation defined if the first argument is '-n' or if any
# argument contains a '\', so use printf instead of echo.
printf '%s\n' "VAR=${VAR}"

Đây là một cách thuận tiện để cho phép người dùng tập lệnh shell của bạn ghi đè cài đặt mà không cần chỉnh sửa tập lệnh. (Tuy nhiên, các đối số dòng lệnh sẽ tốt hơn vì bạn không gặp rủi ro về hành vi không mong muốn nếu người dùng tình cờ có biến bạn sử dụng trong môi trường xuất của họ.) Đây là cách người dùng ghi đè cài đặt:

VAR="other value" ./script

Các ${VAR=value}cú pháp nói với bộ VARđể valuenếu VARchưa được thiết lập, sau đó mở rộng đến các giá trị của biến. Vì chúng ta không quan tâm đến giá trị của biến, nên nó được truyền dưới dạng đối số cho lệnh no-op :để loại bỏ nó.

Mặc dù :là lệnh no-op, việc mở rộng được thực hiện bởi shell (không phải :lệnh!) Trước khi chạy :lệnh để việc gán biến vẫn xảy ra (nếu có thể).

Nó cũng có thể được chấp nhận để sử dụng truehoặc một số lệnh khác thay thế :, nhưng mã trở nên khó đọc hơn vì ý định không rõ ràng.

Kịch bản sau đây cũng sẽ hoạt động:

#!/bin/sh

# print the value of the VAR variable.  Note that POSIX says the behavior
# of echo is implementation defined if the first argument is '-n' or if any
# argument contains a '\', so use printf instead of echo.
printf '%s\n' "VAR=${VAR=default value}"

Nhưng ở trên là khó khăn hơn nhiều để duy trì. Nếu một dòng sử dụng ${VAR}được thêm vào phía trên printfdòng đó , việc mở rộng gán mặc định phải được di chuyển. Nếu nhà phát triển quên di chuyển nhiệm vụ đó, một lỗi sẽ được đưa ra.

Một cái gì đó để đặt trong một khối điều kiện trống

Các khối điều kiện trống thường nên tránh, nhưng đôi khi chúng hữu ích:

if some_condition; then
    # todo:  implement this block of code; for now do nothing.
    # the colon below is a no-op to prevent syntax errors
    :
fi

Một số người lập luận rằng việc có một ifkhối thực sự trống có thể làm cho mã dễ đọc hơn là phủ nhận bài kiểm tra. Ví dụ:

if [ -f foo ] && bar || baz; then
    :
else
    do_something_here
fi

được cho là dễ đọc hơn:

if ! [ -f foo ] || ! bar && ! baz; then
    do_something_here
fi

Tuy nhiên tôi tin rằng có một vài cách tiếp cận khác tốt hơn một khối thực sự trống rỗng:

  1. Đặt điều kiện trong một hàm:

    exotic_condition() { [ -f foo ] && bar || baz; }
    
    if ! exotic_condition; then
        do_something_here
    fi
  2. Đặt điều kiện bên trong dấu ngoặc nhọn (hoặc dấu ngoặc đơn, nhưng dấu ngoặc đơn sinh ra một quy trình con và mọi thay đổi được thực hiện đối với môi trường bên trong lớp con sẽ không hiển thị bên ngoài lớp con) trước khi phủ định:

    if ! { [ -f foo ] && bar || baz; } then
        do_something_here
    fi
  3. Sử dụng ||thay vì if:

    [ -f foo ] && bar || baz || {
        do_something_here
    }

    Tôi thích cách tiếp cận này khi phản ứng là một lớp lót đơn giản, chẳng hạn như điều kiện khẳng định:

    log() { printf '%s\n' "$*"; }
    error() { log "ERROR: $*" >&2; }
    fatal() { error "$@"; exit 1; }
    
    [ -f foo ] && bar || baz || fatal "condition not met"

1

Trong lớp vỏ trước bourne cũ trong các phiên bản UNIX cổ, :lệnh ban đầu được dự định để chỉ định các nhãn cho goto(đó là một lệnh riêng rẽ vào đầu vào nơi tìm thấy nhãn, vì vậy các nhãn không thể là một cú pháp riêng biệt mà shell know about. ifcũng là một lệnh riêng biệt.) Nó nhanh chóng được sử dụng cho các bình luận, trước khi có một cú pháp bình luận ( #được sử dụng cho backspace) và ngày nay là xung quanh để tương thích nhiều như mọi thứ.


0

Ngoài việc sử dụng nó như một câu lệnh không làm gì, bạn có thể sử dụng nó để nhận xét các câu lệnh đơn bằng cách biến chúng thành các đối số cho :.


Nó sẽ không chính xác là một nhận xét vì : echo write this line > myfilevẫn sẽ tạo một tệp trống.
Arcege

5
Như đã giải thích trong các liên kết trong câu hỏi, :KHÔNG một cơ chế cho ý kiến đầy đủ.
jw013
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.