Những gì mà Thử nghiệm $ 2 && có nghĩa là gì trong tập lệnh bash này?


8

Tôi đang xem một tập lệnh bash với đoạn mã sau:

#!/bin/sh

set -e # Exit if any command fails

# If multiple args given run this script once for each arg
test $2 && {
  for arg in $@
    do $0 $arg
  done
  exit
}
.
.
.

Như đã đề cập trong bình luận, mục đích là "chạy tập lệnh trên mỗi đối số nếu có nhiều hơn một" Tôi có một số mã người chạy để kiểm tra điều này:

# install-unit
# If multiple args given run this script once for each arg
test $2 && {
  echo "\$0: $0"
  echo "\$1: $1"
  echo "\$2: $2"
  echo "test \$2 && is true"
}

test $2 && {
# note this is just a regular bash for loop
# http://goo.gl/ZHpBvT
  for arg in $@
    do $0 $arg
  done
  exit
}

echo "running: $1"

cung cấp cho những điều sau đây:

sh  ./multiple-run.sh cats and dogs and stuff
$0: ./multiple-run.sh
$1: cats
$2: and
test $2 && is true
running: cats
running: and
running: dogs
running: and
running: stuff
sh  ./multiple-run.sh cats
running: cats

Tôi muốn ai đó giải nén test $2 && {}phần và exitvỏ được tích hợp. Lúc đầu, tôi đã nghĩ rằng việc test $2kiểm tra xem có nhiều đối số hơn không, và có lẽ là vậy, nhưng có vẻ lạ vì dường như có hai biểu thức cách nhau bởi "và" &&, và tôi cũng chỉ bối rối không biết cái tích hợp này làm gì exit.

Giải thích tiếng Anh đơn giản với các ví dụ và tài liệu tham khảo được đánh giá cao :)

Cảm ơn!

Chỉnh sửa: Cảm ơn Stéphane Chazelas vì điều đó. Đối với những ai đang bối rối bởi sự thay đổi trong cú pháp vòng lặp, hãy kiểm tra số 3 trong này geekstuff bài viết. Để tóm tắt:

echo "positional parameters"
for arg do
  echo "$arg"
done

echo "\nin keyword"
for arg in "$@"
  do echo "$arg"
done

cho chúng tôi:

 ./for-loop-positional.sh cats and dogs          
positional parameters
cats
and
dogs

in keyword
cats
and
dogs

Câu trả lời:


14

Đó là một cách viết không chính xác:

#!/bin/sh -

set -e # Exit if any command fails

# If multiple args given run this script once for each arg
if [ "$#" -gt 1 ]; then
  for arg do
    "$0" "$arg"
  done
  exit
fi

Tôi nghĩ rằng những gì tác giả dự định làm là kiểm tra xem đối số thứ hai có phải là một chuỗi không trống không (điều này không giống với việc kiểm tra xem có nhiều hơn 1 đối số hay không, vì đối số thứ hai có thể được thông qua nhưng là chuỗi trống) .

test somestring

Một dạng ngắn của

test -n somestring

Trả về true nếu somestringkhông phải là chuỗi rỗng. ( test ''trả về false, test anythingelsetrả về true (nhưng hãy cẩn thận test -ttrong một số shell để kiểm tra xem stdout có phải là terminal không)).

Tuy nhiên, tác giả đã quên các trích dẫn xung quanh biến (trên tất cả các biến thực sự). Điều đó có nghĩa là nội dung của $2nó phải chịu toán tử split + global. Vì vậy, nếu $2chứa các ký tự của $IFS(không gian, tab và xuống dòng theo mặc định) hoặc các ký tự glob ( *, ?, [...]), mà sẽ không hoạt động đúng.

Và nếu $2trống (như khi có ít hơn 2 đối số được thông qua hoặc đối số thứ hai trống), test $2trở thành test, không test ''. testhoàn toàn không nhận được bất kỳ đối số nào (trống hoặc khác).

Rất may trong trường hợp đó, testkhông có đối số trả về sai. Thay vào đó, nó tốt hơn một chút so với giá trị test -n $2trả về (thay vào đó sẽ trở thành test -nđúng test -n -n), do đó mã sẽ xuất hiện để hoạt động trong một số trường hợp.

Tóm lại:

  • để kiểm tra nếu 2 hoặc nhiều đối số được thông qua:

    [ "$#" -gt 1 ]

    hoặc là

    [ "$#" -ge 2 ]
  • để kiểm tra nếu một biến không trống:

    [ -n "$var" ]
    [ "$var" != '' ]
    [ "$var" ]
    

    tất cả đều đáng tin cậy trong việc triển khai POSIX [, nhưng nếu bạn phải xử lý các hệ thống rất cũ, bạn có thể phải sử dụng

    [ '' != "$var" ]

    thay cho việc triển khai của [sặc rằng trên các giá trị của $varnhư =, -t, (...

  • để kiểm tra xem một biến có được xác định không (có thể được sử dụng để kiểm tra xem tập lệnh có được thông qua đối số thứ hai hay không, nhưng sử dụng $#sẽ dễ đọc hơn và thành ngữ hơn nhiều):

    [ "${var+defined}" = defined ]

(hoặc tương đương với testbiểu mẫu. Sử dụng [bí danh cho testlệnh là phổ biến hơn).


Bây giờ về sự khác biệt giữa cmd1 && cmd2if cmd1; then cmd2; fi.

Cả hai cmd2chỉ chạy nếu cmd1thành công. Sự khác biệt trong trường hợp đó là trạng thái thoát của danh sách lệnh tổng thể sẽ là trạng thái của lệnh cuối cùng được chạy trong &&trường hợp (vì vậy mã lỗi nếu cmd1không trả về đúng (mặc dù điều đó không xảy ra set -eở đây)) các iftrường hợp, đó sẽ là của cmd2hoặc 0 (thành công) nếu cmd2không được chạy.

Vì vậy, trong trường hợp cmd1được sử dụng như một điều kiện (khi thất bại của nó không được coi là một vấn đề ), thì tốt hơn hết iflà sử dụng đặc biệt nếu đó là điều cuối cùng bạn làm trong tập lệnh vì điều đó sẽ xác định trạng thái thoát tập lệnh của bạn. Nó cũng làm cho mã dễ đọc hơn.

Biểu cmd1 && cmd2mẫu thường được sử dụng làm điều kiện như trong:

if cmd1 && cmd2; then...
while cmd1 && cmd2; do...

Đó là trong bối cảnh chúng ta quan tâm đến trạng thái thoát của cả hai lệnh đó.


5

test $2 && some_command bao gồm hai lệnh:

  • test $2đang kiểm tra xem chuỗi sau khi mở rộng đối số thứ hai ( $2) có độ dài khác không hay không, nghĩa là nó nhất thiết phải kiểm tra test -n $2( [ -n $2 ]). Lưu ý rằng, khi bạn không sử dụng dấu ngoặc kép xung quanh $2, testsẽ làm nghẹt các giá trị với khoảng trắng. Bạn nên sử dụng test "$2"ở đây.

  • &&là một toán tử đánh giá ngắn mạch, chỉ ra rằng lệnh sau &&sẽ chỉ được chạy nếu lệnh trước khi thành công tức là có mã thoát 0. Vì vậy, trong ví dụ trên, some_commandsẽ chỉ được chạy nếu test $2thành công tức là nếu chuỗi không -zero chiều dài, sau đó some_commandsẽ được chạy.


1
test "$2"vẫn không an toàn nếu $ 2 là -nhoặc cái gì đó. Ngoài ra, câu trả lời của Stéphane là chính xác rằng đây chỉ là một phương pháp tồi. (ví dụ foo arg1 '' arg3sẽ đánh lừa nó chỉ nghĩ rằng chỉ có một đối số.)
Peter Cordes

2
@PeterCordes, lưu ý rằng cả hai test "$2"test -n "$2"đều đáng tin cậy trong việc triển khai tuân thủ POSIX của test. Bây giờ, đối với những người không tuân thủ test, cả hai sẽ có vấn đề (như một số với test -nvà một số với test -n =điều đó sẽ trả về một lỗi). Trong lịch sử, test -tcó nghĩa là (và được ghi lại) để kiểm tra xem thiết bị xuất chuẩn là thiết bị đầu cuối, vì vậy test "$2"không thể tin cậy được. POSIX đã thay đổi điều đó. ksh93 test -tvẫn kiểm tra thiết bị xuất chuẩn là thiết bị đầu cuối (cho tính di động lạc hậu) nhưng chỉ khi test-tđược xả rác (không phải trong test "$2"hoặc cmd=test; "$cmd" -thoặc test $empty -t).
Stéphane Chazelas
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.