Trả về lỗi trong shellscript thay vì thoát khỏi lỗi


7

Tôi biết đó set -elà bạn của tôi để thoát lỗi. Nhưng phải làm gì nếu tập lệnh có nguồn gốc, ví dụ một hàm được thực thi từ bàn điều khiển? Tôi không muốn đóng giao diện điều khiển do lỗi, tôi chỉ muốn dừng tập lệnh và hiển thị thông báo lỗi.

Tôi có cần kiểm tra $ không? của mỗi lệnh bằng tay để làm cho điều đó có thể?

Dưới đây là một kịch bản ví dụ myScript.shđể hiển thị vấn đề:

#!/bin/sh
set -e

copySomeStuff()
{
    source="$1"
    dest="$2"
    cp -rt "$source" "$dest"
    return 0
}

installStuff()
{
    dest="$1"
    copySomeStuff dir1 "$dest"
    copySomeStuff dir2 "$dest"
    copySomeStuff nonExistingDirectory "$dest"
}

Kịch bản được sử dụng như thế:

$ source myScript.sh
$ installStuff

Điều này sẽ chỉ đóng giao diện điều khiển. Các lỗi hiển thị bởi cpbị mất.


1
Bạn có chắc chắn nó sẽ đóng giao diện điều khiển? Tôi đã cố chạy script như bạn mô tả và nó chấm dứt toàn bộ những thứ ném cplỗi và thoát với 1 mã thoát.
ddnomad

1
@ddnomad set -eĐặt errexittùy chọn trong trình bao. Khi gọi hàm, cplỗi và shell thoát ra cho tôi.
Kusalananda

1
Nếu bạn nguồn mà nó sẽ bỏ qua #!và sẽ áp dụng set -e cho shell gọi, vì vậy sẽ áp dụng cho tất cả các lệnh tiếp theo.
ctrl-alt-delor

Báo giá gấp đôi đầu vào của người dùng của bạn. Không sử dụng ()khi gọi các hàm shell.
Kusalananda

${dest}${source}vẫn không được trích dẫn, và chúng chứa đầu vào của người dùng. Tôi hơi khó tính với bạn ở đây, nhưng bạn cần trích dẫn chúng trong tập lệnh của mình trên máy hoặc những việc đơn giản như sao chép tệp có khoảng trắng trong tên của chúng sẽ không hoạt động. Lấy làm tiếc. Tôi chỉ đang cố gắng đưa ra phản hồi mang tính xây dựng.
Kusalananda

Câu trả lời:


5

Tôi khuyên bạn nên có một tập lệnh mà bạn chạy dưới dạng trình bao con, có thể tìm nguồn tệp để đọc trong định nghĩa hàm. Hãy để tập lệnh đó thiết lập errexittùy chọn shell cho chính nó.

Khi bạn sử dụng sourcetừ dòng lệnh, "tập lệnh" thực sự là vỏ tương tác của bạn. Thoát có nghĩa là chấm dứt phiên shell. Có thể có những cách xoay quanh vấn đề này, nhưng tùy chọn tốt nhất, nếu bạn muốn đặt errexitcho một phiên, sẽ chỉ đơn giản là có:

#!/bin/bash

set -o errexit

source file_with_functions
do_things_using_functions

Lợi ích bổ sung: Sẽ không gây ô nhiễm phiên tương tác với các chức năng.


Ok, đó là công việc, cảm ơn! Đó là một giải pháp dễ đọc hơn và có lẽ nên được ưu tiên trong hầu hết các trường hợp. Tuy nhiên, đối với trường hợp sử dụng đặc biệt của tôi, nó sẽ dẫn đến 6 tệp, vì tôi cung cấp các dịch vụ khác nhau ở đó, hiện được triển khai dưới dạng các chức năng và tôi muốn dính vào 1 tệp vì lý do khác.
Alex

thật không may, nó không hoạt động với tôi - Tôi đang gọi một chức năng được xác định trong tệp shell-RC của mình, vì vậy tôi không thể thực hiện hợp lý nó từ một nơi khác
Xerus

3

Nếu bạn lấy nguồn đó, nó sẽ bỏ qua #!và sẽ áp dụng set -echo shell gọi, vì vậy sẽ áp dụng cho tất cả các lệnh tiếp theo.

bạn có thể buộc một lớp vỏ phụ:

copySomeStuff()
{
    (
        set -e
        source="$1"
        dest="$2"
        cp -r "$source" "$dest"
        return 0
    )
}

Ngoài ra để đảm bảo an toàn:

  • Không sử dụng tất cả tên biến mũ, vì có khả năng va chạm với tên môi trường. (chúng tôi đã có một câu hỏi tuần trước về lý do tại sao một kịch bản không hoạt động, nó có một biến PATH)

  • Sử dụng dấu ngoặc kép.

  • Luôn luôn sử dụng -thoặc -Tlựa chọn cho cp, mv, ln. ví dụ

    • cp -t destination_directory source_files …
    • cp -T source_file destination_file

    lưu ý rằng -t-Tlà các tiện ích mở rộng Gnu và không có sẵn trên một số Unix khác, vì vậy để có tính di động, bạn có thể thay thế -ttùy chọn này:

    • cp source_files … destination_directory/

    Tôi không biết về một hình thức an toàn thay thế cho -T


2
Xin lưu ý cp -t, đây là phần mở rộng GNU không khả dụng trên các hệ thống BSD. Nếu GNU coreutilsđược cài đặt, GNU cpcó thể có sẵn dưới dạng gcp.
Kusalananda

1
set -etrong vỏ phụ hoạt động tuyệt vời, cảm ơn! Cũng cảm ơn cho các gợi ý an toàn.
Alex

Cảm ơn giải pháp này, nó là hoàn hảo. Nó cũng đảm bảo rằng set -ekhông được đặt trong cuộc gọishell
geedoubleya

subshell hoạt động, ngoại trừ stdout và stderr của subshell dường như bị mất: /
Xerus

@Xerus bạn sẽ phải hỏi một câu hỏi mới, cho thấy một ví dụ về những gì bạn đã làm. Subshell không mất stdout hoặc stderr.
ctrl-alt-delor
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.