Làm cách nào để kiểm tra xem một biến có trống hay chỉ chứa khoảng trắng không?


240

Cú pháp bash sau đây xác minh nếu paramkhông trống:

 [[ !  -z  $param  ]]

Ví dụ:

param=""
[[ !  -z  $param  ]] && echo "I am not zero"

Không có đầu ra và tiền phạt của nó.

Nhưng khi paramtrống, ngoại trừ một (hoặc nhiều) ký tự khoảng trắng, thì trường hợp sẽ khác:

param=" " # one space
[[ !  -z  $param  ]] && echo "I am not zero"

"Tôi không phải là không" là đầu ra.

Làm cách nào tôi có thể thay đổi thử nghiệm để xem xét các biến chỉ chứa các ký tự khoảng trắng là trống?


4
Từ man test: -z STRING - the length of STRING is zero. Nếu bạn muốn xóa tất cả các khoảng trắng trong $param, hãy sử dụng${param// /}
dchirikov

6
WOW không có một trim()chức năng đơn giản nào được tích hợp sẵn cho bất kỳ * nix nào cả? Vì vậy, nhiều cách hack khác nhau để đạt được điều gì đó thật đơn giản ...
ADTC

6
@ADTC $ (sed 's / ^ \ s + | \ s + $ // g' <<< $ chuỗi) có vẻ đơn giản với tôi. Tại sao phải phát minh lại bánh xe?
jbowman

3
Bởi vì nó có thể sáng hơn
Inversus

1
Chỉ cần lưu ý rằng [[ ! -z $param ]]tương đương với test ! -z $param.

Câu trả lời:


292

Đầu tiên, lưu ý rằng -zbài kiểm tra rõ ràng dành cho:

độ dài của chuỗi bằng không

Đó là, một chuỗi chỉ chứa không gian nên không thể đúng dưới -z, bởi vì nó có chiều dài khác không.

Những gì bạn muốn là loại bỏ khoảng trắng khỏi biến bằng cách sử dụng mở rộng tham số thay thế mẫu :

[[ -z "${param// }" ]]

Điều này mở rộng parambiến và thay thế tất cả các kết quả khớp của mẫu (một khoảng trắng) bằng không có gì, do đó, một chuỗi chỉ có khoảng trắng trong đó sẽ được mở rộng thành một chuỗi trống.


Sự thú vị của cách thức hoạt động${var/pattern/string}thay thế trận đấu dài nhất đầu tiên patternvới string. Khi patternbắt đầu bằng /(như trên) thì nó thay thế tất cả các trận đấu. Vì thay thế là trống, chúng tôi có thể bỏ qua giá trị cuối cùng /stringgiá trị:

$ {tham số / mẫu / chuỗi}

Các mô hình được mở rộng để tạo ra một mô hình giống như trong việc mở rộng tên tập tin. Tham số được mở rộng và khớp mẫu dài nhất so với giá trị của nó được thay thế bằng chuỗi . Nếu mẫu bắt đầu bằng '/', tất cả các kết quả khớp của mẫu được thay thế bằng chuỗi . Thông thường chỉ có trận đấu đầu tiên được thay thế. ... Nếu chuỗi là null, các trận đấu của mô hình được xóa và / sau mô hình có thể được bỏ qua.

Sau khi tất cả điều đó, chúng tôi kết thúc với ${param// }để xóa tất cả các không gian.

Lưu ý rằng mặc dù có trong ksh(nơi nó bắt nguồn) zshbash, cú pháp đó không phải là POSIX và không nên được sử dụng trong shcác tập lệnh.


sử dụng sedhọ nói ... chỉ echolà biến họ đã nói ...
mikezter

1
Hừm. Nếu ${var/pattern/string}đó không phải là [[ -z ${param/ /} ]]không gian giữa các dấu gạch chéo sẽ là mẫu và không có gì sau đó là chuỗi?
Jesse Chisholm

@JesseChisholm "Vì thay thế trống, chúng tôi có thể bỏ qua giá trị cuối cùng / và chuỗi"; "Nếu chuỗi là null, các trận đấu của mô hình được xóa và / sau mô hình có thể được bỏ qua."
Michael Homer

6
@MichaelHomer Câu trả lời [[ -z "${param// }" ]]chắc chắn trông giống như patterntrống rỗng và replacement stringlà một không gian duy nhất. AH! Tôi thấy nó bây giờ if pattern begins with a slashtôi đã bỏ lỡ phần đó. vì vậy mô hình là / và chuỗi bị bỏ đi và do đó trống rỗng. Cảm ơn.
Jesse Chisholm

bash note: nếu chạy trong set -o nounset (set -u) và param là unset (chứ không phải null hoặc lấp đầy khoảng trắng), thì điều này sẽ tạo ra lỗi biến không liên kết. (đặt + u; .....) sẽ miễn bài kiểm tra này.
Brian Chrisman

31

Cách dễ dàng để kiểm tra xem một chuỗi chỉ chứa các ký tự trong một bộ được ủy quyền là kiểm tra sự hiện diện của các ký tự trái phép. Do đó, thay vì kiểm tra xem chuỗi chỉ chứa khoảng trắng, hãy kiểm tra xem chuỗi có chứa một số ký tự không phải là khoảng trắng hay không. Trong bash, ksh hoặc zsh:

if [[ $param = *[!\ ]* ]]; then
  echo "\$param contains characters other than space"
else
  echo "\$param consists of spaces only"
fi

Chỉ bao gồm các không gian, bao gồm các trường hợp của một không gian trống (hoặc không đặt).

Bạn có thể muốn kiểm tra bất kỳ ký tự khoảng trắng. Sử dụng [[ $param = *[^[:space:]]* ]]để sử dụng cài đặt ngôn ngữ hoặc bất kỳ danh sách rõ ràng nào về các ký tự khoảng trắng bạn muốn kiểm tra, ví dụ: [[ $param = *[$' \t\n']* ]]để kiểm tra không gian, tab hoặc dòng mới.

Kết hợp một chuỗi với một mẫu với =bên trong [[ … ]]là một phần mở rộng ksh (cũng có trong bash và zsh). Trong bất kỳ kiểu Bourne / POSIX nào, bạn có thể sử dụng casecấu trúc để khớp chuỗi với mẫu. Lưu ý rằng các mẫu shell tiêu chuẩn sử dụng !để phủ định một bộ ký tự, thay vì ^giống như trong hầu hết các cú pháp biểu thức thông thường.

case "$param" in
  *[!\ ]*) echo "\$param contains characters other than space";;
  *) echo "\$param consists of spaces only";;
esac

Để kiểm tra các ký tự khoảng trắng, $'…'cú pháp dành riêng cho ksh / bash / zsh; bạn có thể chèn các ký tự này vào tập lệnh của mình theo nghĩa đen (lưu ý rằng một dòng mới sẽ phải nằm trong dấu ngoặc kép, vì dấu gạch chéo ngược + dòng mới mở rộng thành không có gì) hoặc tạo chúng, ví dụ:

whitespace=$(printf '\n\t ')
case "$param" in
  *[!$whitespace]*) echo "\$param contains non-whitespace characters";;
  *) echo "\$param consists of whitespace only";;
esac

Điều này gần như tuyệt vời - nhưng, cho đến nay, nó không trả lời câu hỏi như đã hỏi. Hiện tại nó có thể cho bạn biết sự khác biệt giữa biến shell unset hoặc rỗng và biến chỉ chứa khoảng trắng. Nếu bạn chấp nhận đề xuất của tôi, bạn sẽ thêm biểu mẫu mở rộng param chưa được hội tụ bởi bất kỳ câu trả lời nào khác kiểm tra rõ ràng một biến shell để được đặt - ý tôi là ${var?not set}- vượt qua bài kiểm tra đó và sống sót sẽ đảm bảo rằng một biến phù hợp với bạn *) casesẽ có kết quả rõ ràng trả lời, ví dụ.
mikeerv

Sửa chữa - trên thực tế, điều này sẽ trả lời câu hỏi ban đầu, nhưng nó đã được chỉnh sửa theo cách mà về cơ bản nó thay đổi ý nghĩa của câu hỏi và do đó làm mất hiệu lực câu trả lời của bạn.
mikeerv

Bạn cần [[ $param = *[!\ ]* ]]trong mksh.
Stéphane Chazelas

20

POSIXly:

case $var in
  (*[![:blank:]]*) echo '$var contains non blank';;
  (*) echo '$var contains only blanks or is empty or unset'
esac

Để phân biệt giữa trống , không trống , trống , không đặt :

case ${var+x$var} in
  (x) echo empty;;
  ("") echo unset;;
  (x*[![:blank:]]*) echo non-blank;;
  (*) echo blank
esac

[:blank:]dành cho các ký tự khoảng cách ngang (không gian và tab trong ASCII, nhưng có thể có thêm một vài địa điểm của bạn; một số hệ thống sẽ bao gồm không gian không phá vỡ (nếu có), một số sẽ không). Nếu bạn cũng muốn các ký tự khoảng cách dọc (như dòng mới hoặc nguồn cấp dữ liệu mẫu), hãy thay thế [:blank:]bằng [:space:].


POSIXly là lý tưởng bởi vì nó sẽ hoạt động với dấu gạch ngang (được cho là tốt hơn).
Ken Sharp

zshDường như có vấn đề với [![:blank:]], nó nâng lên :blà sửa đổi không hợp lệ. Trong shkshthi đua, nó nâng cao sự kiện không được tìm thấy cho[
cuonglm

@cuonglm, bạn đã thử làm gì? var=foo zsh -c 'case ${var+x$var} in (x*[![:blank:]]*) echo x; esac'là được cho tôi Sự kiện không được tìm thấy sẽ chỉ dành cho các vỏ tương tác.
Stéphane Chazelas

@ StéphaneChazelas: Ah, đúng rồi, tôi đã thử với các shell tương tác. Công cụ :bsửa đổi không hợp lệ là một chút lạ.
cuonglm

1
@FranklinYu, trên OS / X shđược bashxây dựng cùng --enable-xpg-echo-default--enable-strict-posix-defaultdo đó phải tuân thủ Unix (bao gồm echo '\t'xuất ra một tab).
Stéphane Chazelas

4

Lý do duy nhất còn lại để viết một kịch bản shell, thay vì một kịch bản bằng ngôn ngữ kịch bản tốt , là nếu tính di động cực đoan là mối quan tâm quá mức. Di sản /bin/shlà điều duy nhất bạn có thể chắc chắn là bạn có, nhưng ví dụ Perl có nhiều khả năng có sẵn đa nền tảng hơn Bash. Do đó, không bao giờ viết các tập lệnh shell sử dụng các tính năng không thực sự phổ biến - và hãy nhớ rằng một số nhà cung cấp Unix độc quyền đã đóng băng môi trường shell của họ trước POSIX.1-2001 .

Có một cách di động để thực hiện bài kiểm tra này, nhưng bạn phải sử dụng tr:

[ "x`printf '%s' "$var" | tr -d "$IFS"`" = x ]

(Giá trị mặc định của $IFS, một cách thuận tiện, là khoảng trắng, tab và dòng mới.)

(Bản printfdựng sẵn có khả năng di động rất nhanh, nhưng việc dựa vào nó sẽ ít rắc rối hơn nhiều so với việc tìm ra biến thể nào của echobạn.)


1
Đó là một chút phức tạp. Cách cầm tay thông thường để kiểm tra xem một chuỗi chứa các ký tự nhất định là case .
Gilles

@Gilles Tôi không nghĩ có thể viết một casequả địa cầu để phát hiện một chuỗi trống hoặc chỉ chứa các ký tự khoảng trắng. (Nó sẽ là dễ dàng với một regexp, nhưng casekhông làm regexps Các cấu trúc trong câu trả lời của bạn sẽ không hoạt động nếu bạn quan tâm đến dòng mới..)
Zwol

1
Tôi đã đưa ra khoảng trắng làm ví dụ trong câu trả lời của mình vì đó là những gì câu hỏi yêu cầu. Thật dễ dàng để kiểm tra các ký tự khoảng trắng : case $param in *[![:space:]]*) …. Nếu bạn muốn kiểm tra các IFSký tự, bạn có thể sử dụng mẫu *[$IFS]*.
Gilles

1
@mikeerv Trong một kịch bản phòng thủ thực sự nghiêm túc , bạn rõ ràng đặt IFS <space><tab><newline>ngay từ đầu và sau đó không bao giờ chạm vào nó nữa. Tôi nghĩ rằng đó sẽ là một sự xao lãng.
zwol

1
Về các biến trong các mẫu, tôi nghĩ rằng nó hoạt động trong tất cả các phiên bản Bourne và tất cả các vỏ POSIX; nó sẽ yêu cầu thêm mã để phát hiện các mẫu trường hợp và tắt mở rộng biến và tôi không thể nghĩ ra lý do để làm điều đó. Tôi có một vài ví dụ về nó .profileđã được thực hiện bởi nhiều shell Bourne. Tất nhiên [$IFS]sẽ không làm những gì đã dự định nếu IFScó một số giá trị không mặc định nhất định (trống (hoặc chưa đặt), có chứa ], bắt đầu bằng !hoặc ^).
Gilles

4
if [[ -n "${variable_name/[ ]*\n/}" ]]
then
    #execute if the the variable is not empty and contains non space characters
else
    #execute if the variable is empty or contains only spaces
fi

Điều này được loại bỏ bất kỳ nhân vật không gian trắng, không chỉ một không gian, phải không? cảm ơn
Alexander Mills

0

Để kiểm tra nếu biến trống hoặc chứa khoảng trắng, bạn cũng có thể sử dụng mã này:

${name:?variable is empty}

2
Tôi nghĩ rằng câu trả lời của bạn bị hạ thấp bởi vì "hoặc chứa khoảng trắng" là không đúng.
Bernhard

hãy cẩn thận - giết chết lớp vỏ hiện tại. Tốt hơn là đặt nó trong một (: $ {subshell?}). Ngoài ra - điều đó sẽ ghi vào stderr - bạn có thể muốn chuyển hướng. Và quan trọng nhất là đánh giá giá trị thực của biến nếu nó không được đặt hoặc không có giá trị. Nếu có một lệnh trong đó, bạn chỉ cần chạy nó. Tốt nhất là chạy thử nghiệm đó :.
mikeerv

Làm thế nào nó sẽ giết vỏ. và bạn cũng có thể kiểm tra điều này, đầu tiên xác định name=aaaasau đó chạy lệnh này, echo ${name:?variable is empty}nó sẽ in giá trị của tên biến và bây giờ chạy lệnh này, echo ${name1:?variable is empty}nó sẽ in -sh: name1: biến là trống
pratik

Có - echo ${name:?variable is empty}sẽ đánh giá giá $nametrị không null hoặc nó sẽ giết vỏ. Vì vậy, với cùng một lý do bạn không nên làm gì prompt > $randomvarcả ${var?}- nếu có lệnh trong đó, nó có thể sẽ chạy - và bạn không biết nó là gì. Một vỏ tương tác không phải thoát ra - đó là lý do tại sao bạn không. Tuy nhiên, nếu bạn muốn xem một số thử nghiệm, có rất nhiều trong số chúng ở đây. . Cái này không dài lắm đâu ...
mikeerv

0

Để kiểm tra xem một biến không được đặt, trống (có giá trị null) hoặc chỉ chứa khoảng trắng:

 [ -z "${param#"${param%%[! ]*}"}" ] && echo "empty"

Giải trình:

  • Việc mở rộng ${param%%[! ]*}loại bỏ từ không gian đầu tiên đến cuối.
  • Điều đó chỉ để lại những không gian hàng đầu.
  • Việc mở rộng tiếp theo sẽ loại bỏ các khoảng trắng hàng đầu khỏi biến.
  • Nếu kết quả là trống, thì biến đó là trống để bắt đầu.
  • Hoặc, nếu biến không trống, loại bỏ các khoảng trắng hàng đầu làm cho nó trống.
  • Nếu kết quả là trống -z, sau đó echo empty.

Trên đây là tương thích POSIX và chạy trong bất kỳ hậu duệ Bourne.

Nếu bạn muốn mở rộng điều đó sang các tab, hãy bao gồm một tab rõ ràng:

 [ -z "${param#"${param%%[!     ]*}"}" ] && echo "empty"

hoặc sử dụng một biến với các ký tự mà bạn cần loại bỏ:

 var=$' \t'   # in ksh, bash, zsh
 [ -z "${param#"${param%%[!$var]*}"}" ] && echo "empty"

hoặc bạn có thể sử dụng một số "biểu thức lớp ký tự" POSIX (trống có nghĩa là khoảng trắng và tab):

 [ -z "${param#"${param%%[![:blank:]]*}"}" ] && echo "empty"

Nhưng điều đó sẽ thất bại trong mksh, lksh và posh.


-1

Thử đi:

$ [[ -z \`echo $n\` ]] && echo zero

zero
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.