Kiểm tra nếu một lệnh xuất ra một chuỗi rỗng


237

Làm thế nào tôi có thể kiểm tra nếu một lệnh xuất ra một chuỗi rỗng?


7
Một lệnh không trả về một chuỗi (nó trả về một mã thoát số nguyên nhỏ, thường là 0 cho thành công và 1 hoặc 2 khi thất bại), nhưng nó có thể xuất ra một số dữ liệu, để đặt trong một chuỗi.
Basile Starynkevitch

@BasileStarynkevitch đây là thứ tôi cần. Tôi biết lệnh trả về mã thoát. Nhưng mã thoát lệnh của tôi luôn là 0. vì vậy tôi cần kiểm soát đầu ra
barp

1
Bạn nên đọc một số hướng dẫn về kịch bản Bash tldp.org/LDP/abs/html
Basile Starynkevitch

Joey Hess có một tiện ích ifnecho việc này. joeyh.name/code/moreutils
tripleee

Câu trả lời:


309

Trước đây, câu hỏi hỏi làm thế nào để kiểm tra xem có tập tin nào trong một thư mục không. Đoạn mã sau đạt được điều đó, nhưng hãy xem câu trả lời của rsp để có giải pháp tốt hơn.


Đầu ra trống

Các lệnh không trả về giá trị - chúng xuất ra chúng. Bạn có thể nắm bắt đầu ra này bằng cách sử dụng thay thế lệnh ; ví dụ $(ls -A). Bạn có thể kiểm tra chuỗi không trống trong Bash như thế này:

if [[ $(ls -A) ]]; then
    echo "there are files"
else
    echo "no files found"
fi

Lưu ý rằng tôi đã sử dụng -Achứ không phải -avì nó bỏ qua các mục nhập thư mục hiện tại ( .) và cha ( ..) tượng trưng .

Lưu ý: Như đã chỉ ra trong các nhận xét, thay thế lệnh không nắm bắt được các dòng mới . Do đó, nếu các kết quả đầu ra lệnh chỉ dòng mới, sự thay thế sẽ nắm bắt được gì cả và kiểm tra sẽ trả về false. Mặc dù rất khó xảy ra, nhưng điều này là có thể trong ví dụ trên, vì một dòng mới là tên tệp hợp lệ! Thêm thông tin trong câu trả lời này .


Mã thoát

Nếu bạn muốn kiểm tra xem lệnh đã hoàn thành thành công hay chưa, bạn có thể kiểm tra $?, trong đó có chứa mã thoát của lệnh cuối cùng (không cho thành công, khác không cho thất bại). Ví dụ:

files=$(ls -A)
if [[ $? != 0 ]]; then
    echo "Command failed."
elif [[ $files ]]; then
    echo "Files found."
else
    echo "No files found."
fi

Thêm thông tin ở đây .


4
Bạn có thể bỏ qua -n , vì vậy nó sẽ chỉ làif [[ $(ls -A) ]]; then
người dùng

6
Phương thức này cung cấp sai cho các lệnh chỉ xuất ra dòng mới.
Ciro Santilli 郝海东 冠状 病 事件

89

TL; DR

if [[ $(ls -A | head -c1 | wc -c) -ne 0 ]]; then ...; fi

Cảm ơn netj về một gợi ý để cải thiện bản gốc của tôi:
if [[ $(ls -A | wc -c) -ne 0 ]]; then ...; fi


Đây là một câu hỏi cũ nhưng tôi thấy ít nhất hai điều cần cải thiện hoặc ít nhất là làm rõ.

Vấn đề đầu tiên

Vấn đề đầu tiên tôi thấy là hầu hết các ví dụ được cung cấp ở đây đơn giản là không hoạt động . Họ sử dụng các lệnh ls -alls -Al- cả hai đều xuất ra các chuỗi không trống trong các thư mục trống. Những ví dụ này luôn báo cáo rằng có các tệp ngay cả khi không có .

Vì lý do đó, bạn chỉ nên sử dụng ls -A- Tại sao mọi người muốn sử dụng công -ltắc có nghĩa là "sử dụng định dạng danh sách dài" khi tất cả những gì bạn muốn là kiểm tra xem có đầu ra nào hay không?

Vì vậy, hầu hết các câu trả lời ở đây chỉ đơn giản là không chính xác.

Vấn đề thứ hai

Vấn đề thứ hai là trong khi một số câu trả lời làm việc tốt (những người không sử dụng ls -alhoặc ls -Alnhưng ls -Athay vào đó) tất cả họ đều làm một cái gì đó như thế này:

  1. chạy một lệnh
  2. đệm toàn bộ đầu ra của nó trong RAM
  3. chuyển đổi đầu ra thành một chuỗi đơn lớn
  4. so sánh chuỗi đó với một chuỗi rỗng

Những gì tôi sẽ đề nghị làm thay vào đó sẽ là:

  1. chạy một lệnh
  2. đếm các ký tự trong đầu ra của nó mà không lưu trữ chúng
    • hoặc thậm chí tốt hơn - đếm số lượng tối đa 1 ký tự using head -c1
      (cảm ơn netj vì đã đăng ý tưởng này trong các bình luận bên dưới)
  3. so sánh số đó với số không

Vì vậy, ví dụ, thay vì:

if [[ $(ls -A) ]]

Tôi sẽ dùng:

if [[ $(ls -A | wc -c) -ne 0 ]]
# or:
if [[ $(ls -A | head -c1 | wc -c) -ne 0 ]]

Thay vì:

if [ -z "$(ls -lA)" ]

Tôi sẽ dùng:

if [ $(ls -lA | wc -c) -eq 0 ]
# or:
if [ $(ls -lA | head -c1 | wc -c) -eq 0 ]

và như thế.

Đối với đầu ra nhỏ, nó có thể không phải là vấn đề nhưng đối với đầu ra lớn hơn, sự khác biệt có thể là đáng kể:

$ time [ -z "$(seq 1 10000000)" ]

real    0m2.703s
user    0m2.485s
sys 0m0.347s

So sánh nó với:

$ time [ $(seq 1 10000000 | wc -c) -eq 0 ]

real    0m0.128s
user    0m0.081s
sys 0m0.105s

Và thậm chí tốt hơn:

$ time [ $(seq 1 10000000 | head -c1 | wc -c) -eq 0 ]

real    0m0.004s
user    0m0.000s
sys 0m0.007s

Ví dụ đầy đủ

Ví dụ cập nhật từ câu trả lời của Will Vousden:

if [[ $(ls -A | wc -c) -ne 0 ]]; then
    echo "there are files"
else
    echo "no files found"
fi

Cập nhật lại sau khi đề xuất bởi netj :

if [[ $(ls -A | head -c1 | wc -c) -ne 0 ]]; then
    echo "there are files"
else
    echo "no files found"
fi

Cập nhật bổ sung bởi jakeonfire :

grepsẽ thoát với một thất bại nếu không có trận đấu. Chúng ta có thể tận dụng điều này để đơn giản hóa cú pháp một chút:

if ls -A | head -c1 | grep -E '.'; then
    echo "there are files"
fi

if ! ls -A | head -c1 | grep -E '.'; then
    echo "no files found"
fi

Loại bỏ khoảng trắng

Nếu lệnh mà bạn đang kiểm tra có thể xuất ra một số khoảng trắng mà bạn muốn coi là một chuỗi rỗng, thì thay vì:

| wc -c

bạn đã có thể sử dụng:

| tr -d ' \n\r\t ' | wc -c

hoặc với head -c1:

| tr -d ' \n\r\t ' | head -c1 | wc -c

hoặc điều tương tự.

Tóm lược

  1. Đầu tiên, sử dụng một lệnh hoạt động .

  2. Thứ hai, tránh lưu trữ không cần thiết trong RAM và xử lý dữ liệu có khả năng rất lớn.

Câu trả lời không xác định rằng đầu ra luôn nhỏ, do đó, khả năng đầu ra lớn cũng cần được xem xét.


3
[[ $(... | head -c | wc -c) -gt 0 ]]hoặc [[ -n $(... | head -c1) ]]là tốt hơn bởi vì wc -csẽ phải tiêu thụ toàn bộ đầu ra của lệnh.
netj

@netj Đây là một ý tưởng rất tốt. Xem câu trả lời cập nhật của tôi - Tôi đã thêm đề xuất của bạn. Cảm ơn rất nhiều!
rsp

Bất kỳ ý tưởng về làm thế nào để có được một đầu ra?
fahrradflucht

Tôi nghĩ rằng bản gốc (không có đầu) là tốt hơn trong trường hợp chung. Không phải lúc nào cũng nên giết lệnh ngay khi nó bắt đầu viết đầu ra!
stk

@stk Hầu hết các chương trình sẽ thoát an toàn sau khi nhận được tín hiệu tiêu diệt.
ngày

40
if [ -z "$(ls -lA)" ]; then
  echo "no files found"
else
  echo "There are files"
fi

Điều này sẽ chạy lệnh và kiểm tra xem đầu ra được trả về (chuỗi) có độ dài bằng không. Bạn có thể muốn kiểm tra các trang hướng dẫn 'kiểm tra' để tìm các cờ khác.

Sử dụng "" xung quanh đối số đang được kiểm tra, nếu không, kết quả trống sẽ dẫn đến lỗi cú pháp vì không có đối số thứ hai (để kiểm tra) được đưa ra!

Lưu ý: ls -laluôn luôn trả về ...do đó sử dụng sẽ không hoạt động, xem các trang hướng dẫn ls . Hơn nữa, trong khi điều này có vẻ thuận tiện và dễ dàng, tôi cho rằng nó sẽ dễ dàng bị phá vỡ. Viết một tập lệnh / ứng dụng nhỏ trả về 0 hoặc 1 tùy theo kết quả sẽ đáng tin cậy hơn nhiều!


Bạn có thể thích sử dụng $(thay vì backquote, vì $(làm tổ tốt hơn
Basile Starynkevitch

Bạn nói đúng, tốt hơn hết là sử dụng chúng luôn, mặc dù chúng ta không cần làm tổ ở đây.
Ăn chay

23

Đối với những người muốn một giải pháp độc lập, bash phiên bản độc lập (trên thực tế nên hoạt động trong các trình bao hiện đại khác) và những người thích sử dụng một lớp lót cho các nhiệm vụ nhanh chóng. Chúng ta đi đây!

ls | grep . && echo 'files found' || echo 'files not found'

(lưu ý là một trong những ý kiến ​​được đề cập, ls -alvà trên thực tế, tất cả -l-asẽ trả lại một cái gì đó, vì vậy trong câu trả lời của tôi, tôi sử dụng đơn giảnls


5
Nếu bạn sử dụng grep -q ^thay thế, các ký tự dòng mới cũng sẽ được khớp và grep sẽ không in bất cứ thứ gì ra đầu ra tiêu chuẩn. Hơn nữa, nó sẽ thoát ngay khi nhận được bất kỳ đầu vào nào, thay vì chờ luồng đầu vào kết thúc.
mortehu

Nên bắt đầu bằng ls -Anếu bạn muốn bao gồm các tệp dấu chấm (hoặc thư mục)
wrlee

13

Tài liệu tham khảo Bash

6.4 Biểu thức điều kiện Bash

-z string
     True if the length of string is zero.

-n string
string
     True if the length of string is non-zero.

Bạn có thể sử dụng phiên bản tốc ký:

if [[ $(ls -A) ]]; then
  echo "there are files"
else
  echo "no files found"
fi

1
Có một -z hoặc -n bị thiếu trong ngoặc đơn [[...]].
71GA

4
@ 71GA: Có lẽ điều đó dễ bị bỏ qua, nhưng nếu bạn xem xét kỹ, bạn sẽ thấy đó chỉ stringlà một từ đồng nghĩa với -n string.
người dùng

10

Như Jon Lin đã nhận xét, ls -alsẽ luôn xuất ra (cho ...). Bạn muốn ls -Altránh hai thư mục này.

Ví dụ, bạn có thể đặt đầu ra của lệnh vào một biến shell:

v=$(ls -Al)

Một ký hiệu cũ hơn, không thể lồng được là

v=`ls -Al`

nhưng tôi thích ký hiệu lồng nhau $(...)

Các bạn có thể kiểm tra nếu biến đó không trống

if [ -n "$v" ]; then
    echo there are files
else
    echo no files
fi

Và bạn có thể kết hợp cả hai như if [ -n "$(ls -Al)" ]; then


5

Tôi đoán bạn muốn đầu ra của ls -allệnh, vì vậy trong bash, bạn sẽ có một cái gì đó như:

LS=`ls -la`

if [ -n "$LS" ]; then
  echo "there are files"
else
  echo "no files found"
fi

Điều này không hoạt động: lệnh lskhông bao giờ được thực thi. Bạn chỉ kiểm tra xem việc mở rộng biến LS có trống không.
gniourf_gniourf

1
@gniourf_gniourf - điều gì khiến bạn nghĩ như vậy? Câu thần chú backtick được thừa nhận là không đẹp hoặc linh hoạt như $ (), nhưng nó hoạt động ...
tink

Công -atắc sẽ không bao giờ trả lại nội dung (nghĩa là nó sẽ trả về nội dung cho dù có tệp hay không) vì luôn có các thư mục ẩn ...thư mục hiện diện.
quậy phá

3

Đây là một giải pháp cho các trường hợp cực đoan hơn:

if [ `command | head -c1 | wc -c` -gt 0 ]; then ...; fi

Điều này sẽ làm việc

  • cho tất cả các vỏ Bourne;
  • nếu đầu ra lệnh là tất cả các số 0;
  • hiệu quả bất kể kích thước đầu ra;

Tuy nhiên,

  • lệnh hoặc các quy trình con của nó sẽ bị hủy khi bất cứ thứ gì xuất ra.

1

Tất cả các câu trả lời cho đến nay đều xử lý các lệnh kết thúc và xuất ra một chuỗi không trống.

Hầu hết bị phá vỡ trong các giác quan sau:

  • Họ không giải quyết đúng đắn các lệnh chỉ xuất ra dòng mới;
  • bắt đầu từ Bash≥4.4 hầu hết sẽ spam lỗi tiêu chuẩn nếu lệnh đầu ra null byte (vì chúng sử dụng thay thế lệnh);
  • hầu hết sẽ làm lu mờ luồng đầu ra đầy đủ, vì vậy sẽ đợi cho đến khi lệnh kết thúc trước khi trả lời. Một số lệnh không bao giờ kết thúc (thử, ví dụ, yes).

Vì vậy, để khắc phục tất cả các vấn đề này và để trả lời câu hỏi sau một cách hiệu quả,

Làm thế nào tôi có thể kiểm tra nếu một lệnh xuất ra một chuỗi rỗng?

bạn có thể dùng:

if read -n1 -d '' < <(command_here); then
    echo "Command outputs something"
else
    echo "Command doesn't output anything"
fi

Bạn cũng có thể thêm một số thời gian chờ để kiểm tra xem một lệnh kết quả đầu ra một chuỗi không có sản phẩm nào trong một thời gian nhất định, sử dụng read's -tlựa chọn. Ví dụ: trong khoảng thời gian chờ 2,5 giây:

if read -t2.5 -n1 -d '' < <(command_here); then
    echo "Command outputs something"
else
    echo "Command doesn't output anything"
fi

Ghi chú. Nếu bạn nghĩ rằng bạn cần xác định xem một lệnh có tạo ra một chuỗi không trống hay không, rất có thể bạn có vấn đề XY.


Tôi nghĩ rằng câu trả lời này là khá đánh giá thấp. Tôi đã thực hiện kiểm nghiệm một số các giải pháp ở đây sử dụng 100 lần lặp cho mỗi 3 kịch bản đầu vào ( seq 1 10000000, seq 1 20printf""). Cách tiếp cận duy nhất mà phương pháp đọc này không thắng là với -z "$(command)"cách tiếp cận cho đầu vào trống. Thậm chí sau đó, nó chỉ thua khoảng 10-15%. Họ dường như phá vỡ thậm chí xung quanh seq 1 10. Bất kể, -z "$(command)"cách tiếp cận trở nên quá chậm khi kích thước đầu vào tăng lên mà tôi tránh sử dụng nó cho bất kỳ đầu vào có kích thước thay đổi nào.
abathur

0

Đây là một cách tiếp cận khác để ghi std-out và std-err của một số lệnh một tệp tạm thời, sau đó kiểm tra xem tệp đó có trống không. Một lợi ích của phương pháp này là nó thu được cả hai đầu ra và không sử dụng vỏ phụ hoặc ống dẫn. Những khía cạnh sau này rất quan trọng vì chúng có thể can thiệp vào việc xử lý thoát bẫy bash (ví dụ ở đây )

tmpfile=$(mktemp)
some-command  &> "$tmpfile"
if [[ $? != 0 ]]; then
    echo "Command failed"
elif [[ -s "$tmpfile" ]]; then
    echo "Command generated output"
else
    echo "Command has no output"
fi
rm -f "$tmpfile"

0

Đôi khi bạn muốn lưu kết quả đầu ra, nếu nó không trống, để chuyển nó sang một lệnh khác. Nếu vậy, bạn có thể sử dụng một cái gì đó như

list=`grep -l "MY_DESIRED_STRING" *.log `
if [ $? -eq 0 ]
then
    /bin/rm $list
fi

Bằng cách này, rmlệnh sẽ không bị treo nếu danh sách trống.

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.