Có cách nào để có một chức năng trong tập lệnh bash của tôi tự động chạy trên bất kỳ lỗi lệnh nào không?


12

Tôi đang viết một tập lệnh shell cần thực hiện một loạt các lệnh và mỗi lệnh phụ thuộc vào mỗi lệnh trước đó. Nếu bất kỳ lệnh nào thất bại, toàn bộ tập lệnh sẽ thất bại và tôi gọi một hàm thoát. Tôi có thể kiểm tra mã thoát của mỗi lệnh nhưng tôi tự hỏi liệu có chế độ nào tôi có thể kích hoạt hoặc cách để bash thực hiện điều đó một cách tự động.

Ví dụ: thực hiện các lệnh sau:

cd foo || myfunc
rm a || myfunc
cd bar || myfunc
rm b || myfunc


Có cách nào để tôi có thể báo hiệu cho shell bằng cách nào đó trước khi thực hiện các lệnh này rằng nó sẽ gọi myfunc nếu bất kỳ trong số chúng không thành công, để thay vào đó tôi có thể viết một cái gì đó sạch hơn như:

cd foo
rm a
cd bar
rm b

Câu trả lời:


13

Bạn có thể sử dụng bash bẫy ERR để thoát tập lệnh của mình nếu bất kỳ lệnh nào trả về trạng thái lớn hơn 0 và thực thi chức năng của bạn khi thoát.

Cái gì đó như:

myfunc() {
  echo 'Error raised. Exiting!'
}

trap 'myfunc' ERR

# file does not exist, causing error
ls asd
echo 123

Lưu ý rằng bẫy bash ERR không ẩn set -o errexithoặc set -ekhông phải là POSIX.

ERRbẫy không được thực thi nếu lệnh thất bại là một phần của danh sách lệnh ngay sau từ khóa untilhoặc whiletừ khóa, một phần của kiểm tra theo các từ ifhoặc elifdành riêng, một phần của lệnh được thực thi trong &&hoặc ||liệt kê hoặc nếu trạng thái trả về của lệnh đang được đảo ngược bằng cách sử dụng !.


1

Một biến thể (có lẽ) đơn giản hơn cho câu trả lời được chấp nhận:

  1. Sử dụng set -e để gây ra sự thất bại của một lệnh để hủy bỏ việc thực hiện danh sách.
  2. Đơn giản chỉ cần liệt kê các lệnh của bạn.
  3. Sử dụng if- then- elsetuyên bố để thực hiện lệnh lỗi xử lý của bạn (s). Phần cuối cùng này là một chút khó khăn. Đồng hồ đeo tay:
đặt -e
nếu
    cmd 1                         # vd, cd foo
     cmd 2                         # vd, rm a
     cmd 3                         # vd, thanh cd
     cmd 4                         # vd
sau đó
    đặt + e
    các lệnh để làm khi thành công (nếu có)
khác
    đặt + e
    myfunc
    các lệnh khác để làm khi thất bại (nếu có) 
fi

Phần khó khăn là bạn đặt lệnh của bạn vào các ifphần của if- then- elsechứ không phải là thenphần hoặc elsemột phần. Hãy nhớ lại rằng cú pháp của ifcâu lệnh

nếu  danh sách ; sau đó  liệt kê ; [  danh sách elif ; sau đó  liệt kê ; ] ... [  danh sách khác ; ] 
   ↑↑↑↑
Cái set -evỏ báo rằng, nếu ( ) thất bại, nó không nên tiếp tục và thực thi ( ), và cứ thế xuống dòng. Nếu điều này xảy ra với một lệnh ở cấp độ ngoài cùng của tập lệnh shell, shell sẽ thoát. Tuy nhiên, vì danh sách · · · là một danh sách (ghép) theo sau , nên việc thất bại của bất kỳ lệnh nào trong bốn lệnh đó chỉ khiến toàn bộ danh sách bị lỗi - điều này khiến mệnh đề được thực thi. Nếu tất cả bốn lệnh thành công, mệnh đề được thực thi.cmd1cd foocmd2rm acmd1cmd2cmd3cmd4ifelsethen

Trong cả hai trường hợp, điều đầu tiên bạn nên làm có lẽ là vô hiệu hóa (tắt) etùy chọn bằng cách thực hiện set +e. Nếu không, tập lệnh có thể bị thổi bay ra khỏi nước nếu lệnh myfuncbị lỗi.

set -eđược chỉ định và mô tả trong đặc tả POSIX .


0

Lấy từ của bạn " mọi lệnh đều phụ thuộc vào mỗi lệnh trước đó. Nếu bất kỳ lệnh nào thất bại thì toàn bộ tập lệnh sẽ thất bại " theo nghĩa đen, tôi nghĩ bạn không cần bất kỳ chức năng đặc biệt nào để xử lý lỗi.

Tất cả những gì bạn cần là xâu chuỗi các lệnh của bạn với &&toán tử và ||toán tử, thực hiện chính xác những gì bạn đã viết.

Ví dụ: chuỗi này sẽ bị hỏng và sẽ in "đã xảy ra lỗi" nếu bất kỳ lệnh nào trước đó bị hỏng (bash đọc từ trái sang phải)

cd foo && rm a && cd bar && rm b || echo "something went wrong"

Ví dụ thực tế (tôi đã tạo dir foo, tệp a, thanh dir và tệp b chỉ cho một bản demo thực sự):

gv@debian:/home/gv/Desktop/PythonTests$ cd foo && rm a && cd bar && rm bb || echo "something is wrong"
rm: cannot remove 'bb': No such file or directory
something is wrong #mind the error in the last command

gv@debian:/home/gv/Desktop/PythonTests$ cd foo && rm aa && cd bar && rm b || echo "something is wrong"
rm: cannot remove 'aa': No such file or directory
something is wrong #mind the error in second command in the row

Và cuối cùng nếu tất cả các lệnh đã được thực thi thành công (mã thoát 0), tập lệnh sẽ tiếp tục:

gv@debian:/home/gv/Desktop/PythonTests$ cd foo && rm a && cd bar && rm b || echo "something is wrong"
gv@debian:/home/gv/Desktop/PythonTests/foo/bar$ 
# mind that the error message is not printed since all commands were successful.

Điều quan trọng cần nhớ là với việc sử dụng && lệnh tiếp theo được thực thi nếu lệnh trước đó thoát với mã 0, điều đó có nghĩa là bash có nghĩa là thành công.

Nếu bất kỳ lệnh nào bị sai trong chuỗi thì lệnh / script / bất cứ điều gì sau | | sẽ được thực thi.

Và chỉ với bản ghi, Nếu bạn cần thực hiện các hành động khác nhau tùy thuộc vào lệnh bị hỏng, bạn cũng có thể thực hiện với tập lệnh cổ điển bằng cách theo dõi giá trị $?báo cáo mã thoát của lệnh chính xác trước đó (trả về 0 nếu lệnh được thực thi thành công hoặc số dương khác nếu lệnh thất bại)

Thí dụ:

for comm in {"cd foo","rm a","cd bbar","rm b"};do  #mind the error in third command
eval $comm
    if [[ $? -ne 0 ]];then 
        echo "something is wrong in command $comm"
        break
    else 
    echo "command $comm executed succesful"
    fi
done

Đầu ra:

command cd foo executed succesfull
command rm a executed succesfull
bash: cd: bbar: No such file or directory
something is wrong in command cd bbar

Mẹo: Bạn có thể chặn thông báo "bash: cd: bbar: Không có tệp nào như vậy ..." bằng cách áp dụng eval $comm 2>/dev/null

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.