Làm cách nào tôi có thể sử dụng bash's if test và tìm các lệnh cùng nhau?


25

Tôi có một thư mục với các bản ghi sự cố và tôi muốn sử dụng một câu lệnh có điều kiện trong tập lệnh bash dựa trên lệnh find.

Các tệp nhật ký được lưu trữ ở định dạng này:

/var/log/crashes/app-2012-08-28.log
/var/log/crashes/otherapp-2012-08-28.log

Tôi muốn câu lệnh if chỉ trả về true nếu có nhật ký sự cố cho một ứng dụng cụ thể đã được sửa đổi trong 5 phút qua. Các findlệnh mà tôi sẽ sử dụng là:

find /var/log/crashes -name app-\*\.log -mmin -5

Tôi không chắc làm thế nào để kết hợp nó vào một iftuyên bố đúng. Tôi nghĩ rằng điều này có thể làm việc:

if [ test `find /var/log/crashes -name app-\*\.log -mmin -5` ] then
 service myapp restart
fi

Có một vài lĩnh vực mà tôi không rõ ràng:

  • Tôi đã xem các cờ if nhưng tôi không chắc nên sử dụng cái nào, nếu có, mà tôi nên sử dụng.
  • Tôi có cần testchỉ thị hay tôi chỉ nên xử lý trực tiếp kết quả của lệnh find hoặc có thể sử dụng find... | wc -lđể lấy số đếm dòng thay thế?
  • Không phải 100% cần thiết để trả lời câu hỏi này, nhưng testlà để kiểm tra mã trả về mà lệnh trả về? Và chúng là loại vô hình - bên ngoài stdout/ stderr? Tôi đọc mantrang này nhưng tôi vẫn chưa rõ về thời điểm sử dụng testvà cách gỡ lỗi.

Câu trả lời thực sự cho trường hợp chung là sử dụng find ... -exec. Cũng xem các lệnh ví dụ bên dưới Tại sao lặp đi lặp lại thực tiễn xấu của đầu ra?
tự đại diện

@Wildcard - thật không may, điều đó không giải quyết được trường hợp chung: nó không hoạt động nếu có nhiều hơn một trận đấu và hành động chỉ cần chạy một lần và nó không hoạt động nếu bạn cần một hành động để chạy khi có không có trận đấu. Cái trước có thể được giải quyết bằng cách sử dụng ... -exec command ';' -quit, nhưng tôi không tin có bất kỳ giải pháp nào cho cái sau ngoài việc phân tích kết quả. Ngoài ra, trong cả hai trường hợp, vấn đề chính với việc phân tích kết quả của find(nghĩa là không thể phân biệt các dấu phân cách với các ký tự trong tên tệp) không được áp dụng, vì bạn không cần phải tìm các dấu phân cách trong các trường hợp này.
Jules

Câu trả lời:


27

[testlà từ đồng nghĩa (trừ [yêu cầu ]), vì vậy bạn không muốn sử dụng [ test:

[ -x /bin/cat ] && echo 'cat is executable'
test -x /bin/cat && echo 'cat is executable'

testtrả về trạng thái thoát không nếu điều kiện là đúng, nếu không thì khác. Điều này thực sự có thể được thay thế bởi bất kỳ chương trình nào để kiểm tra trạng thái thoát của nó, trong đó 0 biểu thị thành công và khác không cho thấy thất bại:

# echoes "command succeeded" because echo rarely fails
if /bin/echo hi; then echo 'command succeeded'; else echo 'command failed'; fi

# echoes "command failed" because rmdir requires an argument
if /bin/rmdir; then echo 'command succeeded'; else echo 'command failed'; fi

Tuy nhiên, tất cả các ví dụ trên chỉ kiểm tra trạng thái thoát của chương trình và bỏ qua đầu ra của chương trình.

Đối với find, bạn sẽ cần phải kiểm tra nếu bất kỳ đầu ra đã được tạo ra. -nkiểm tra một chuỗi không trống:

if [[ -n $(find /var/log/crashes -name "app-*.log" -mmin -5) ]]
then
    service myapp restart
fi

Một danh sách đầy đủ các đối số kiểm tra có sẵn bằng cách gọi help testvào bashdòng lệnh.

Nếu bạn đang sử dụng bash(và không sh), bạn có thể sử dụng [[ condition ]], hoạt động dễ dự đoán hơn khi có không gian hoặc các trường hợp đặc biệt khác trong tình trạng của bạn. Nếu không, nó thường giống như sử dụng [ condition ]. Tôi đã sử dụng [[ condition ]]trong ví dụ này, như tôi làm bất cứ khi nào có thể.

Tôi cũng thay đổi `command`đến $(command), mà cũng thường cư xử tương tự, nhưng là đẹp hơn với các lệnh lồng nhau.


echocó thể thất bại: thử echo 'oops' > /dev/full.
derobert

Câu trả lời này đánh bại tất cả xung quanh gốc rễ của vấn đề nhưng duyên dáng tránh đề cập chính xác đó là gì.
bahamat

9

findsẽ thoát thành công nếu không có bất kỳ lỗi nào, vì vậy bạn không thể dựa vào trạng thái thoát của nó để biết liệu nó có tìm thấy tệp nào không. Nhưng, như bạn đã nói, bạn có thể đếm xem có bao nhiêu tệp được tìm thấy và kiểm tra số đó.

Nó sẽ là một cái gì đó như thế này:

if [ $(find /var/log/crashes -name 'app-*.log' -mmin -5 | wc -l) -gt 0 ]; then
    ...
fi

test(aka [) không kiểm tra mã lỗi của các lệnh, nó có một cú pháp đặc biệt để thực hiện kiểm tra và sau đó thoát với mã lỗi là 0 nếu thử nghiệm thành công hoặc 1 cách khác. Nó là ifcái kiểm tra mã lỗi của lệnh bạn truyền cho nó và thực thi phần thân của nó dựa trên nó.

Xem man test(hoặc help test, nếu bạn sử dụng bash) và help if(ditto).

Trong trường hợp này, wc -lsẽ xuất ra một số. Chúng tôi sử dụng testtùy chọn của mình -gtđể kiểm tra xem con số đó có lớn hơn không 0. Nếu có, test(hoặc [) sẽ trả về với mã thoát 0. ifsẽ diễn giải mã thoát đó là thành công và nó sẽ chạy mã bên trong phần thân của nó.


1

Điều này sẽ

if [ -n "$(find /var/log/crashes -name app-\*\.log -mmin -5)" ]; then

hoặc là

if test -n "$(find /var/log/crashes -name app-\*\.log -mmin -5)"; then

Các lệnh test[ … ]hoàn toàn đồng nghĩa. Sự khác biệt duy nhất là tên của họ và thực tế [đòi hỏi phải đóng ]như là đối số cuối cùng của nó. Như mọi khi, hãy sử dụng dấu ngoặc kép xung quanh thay thế lệnh, nếu không, đầu ra của findlệnh sẽ được chia thành các từ và ở đây bạn sẽ gặp lỗi cú pháp nếu có nhiều hơn một tệp phù hợp (và khi không có đối số, [ -n ]là đúng , trong khi bạn muốn [ -n "" ]đó là sai).

Trong ksh, bash và zsh nhưng không có trong tro, bạn cũng có thể sử dụng [[ … ]]có các quy tắc phân tích cú pháp khác nhau: [là một lệnh thông thường, trong khi đó [[ … ]]là một cấu trúc phân tích cú pháp khác nhau. Bạn không cần dấu ngoặc kép bên trong [[ … ]](mặc dù chúng không bị tổn thương). Bạn vẫn cần lệnh ;sau.

if [[ -n $(find /var/log/crashes -name app-\*\.log -mmin -5) ]]; then

Điều này có khả năng không hiệu quả: nếu có nhiều tệp trong /var/log/crashes, find sẽ khám phá tất cả. Bạn nên tìm kiếm dừng lại ngay khi nó tìm thấy một trận đấu, hoặc ngay sau đó. Với GNU find (không nhúng Linux, Cygwin), hãy sử dụng -quitchính.

if [ -n "$(find /var/log/crashes -name app-\*\.log -mmin -5 -print -quit)" ]; then

Với các hệ thống khác, đường ống findvào headít nhất sẽ thoát ra ngay sau trận đấu đầu tiên (tìm sẽ chết vì đường ống bị hỏng).

if [ -n "$(find /var/log/crashes -name app-\*\.log -mmin -5 -print | head -n 1)" ]; then

(Bạn có thể sử dụng head -c 1nếu headlệnh của bạn hỗ trợ nó.)


Ngoài ra, sử dụng zsh.

crash_files=(/var/log/crashes/**/app-*.log(mm-5[1]))
if (($#crash_files)); then

1

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

if find /var/log/crashes -name 'app-\*\.log' -mmin -5 | read
then
  service myapp restart
fi

0
find /var/log/crashes -name app-\*\.log -mmin -5 -exec service myapp restart ';' -quit

là một giải pháp thích hợp ở đây.

-exec service myapp restart ';'nguyên nhân findđể gọi lệnh mà bạn muốn chạy trực tiếp, thay vì cần shell để giải thích bất cứ điều gì.

-quitnguyên nhân findđể thoát sau khi xử lý lệnh, do đó ngăn lệnh được thực thi lại nếu có nhiều tệp phù hợp với tiêu chí.

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.