Bagaimana cara menemukan nomor baris di Bash saat terjadi kesalahan?

21

Bagaimana Anda menemukan nomor baris di Bash di mana kesalahan terjadi?

Contoh

Saya membuat skrip sederhana berikut dengan nomor baris untuk menjelaskan apa yang kita butuhkan. Script akan menyalin file dari

cp $file1 $file2
cp $file3 $file4

Ketika salah satu cpperintah gagal maka fungsi akan keluar dengan keluar 1 . Kami ingin menambahkan kemampuan ke fungsi untuk juga mencetak kesalahan dengan nomor baris (misalnya, 8 atau 12).

Apakah ini mungkin?

Contoh skrip

1 #!/bin/bash
2
3
4 function in_case_fail {
5 [[ $1 -ne 0 ]] && echo "fail on $2" && exit 1
6 }
7
8 cp $file1 $file2
9 in_case_fail $? "cp $file1 $file2"
10
11
12 cp $file3 $file4
13 in_case_fail $? "cp $file3 $file4"
14
yael
sumber
Anda dapat menggunakan set -xdan / atau set -vuntuk melacak apa yang telah dieksekusi. Tidak persis apa yang Anda minta, tetapi mungkin akan membantu juga.
Rolf

Jawaban:

29

Daripada menggunakan fungsi Anda, saya akan menggunakan metode ini sebagai gantinya:

$ cat yael.bash
#!/bin/bash

set -eE -o functrace

file1=f1
file2=f2
file3=f3
file4=f4

failure() {
  local lineno=$1
  local msg=$2
  echo "Failed at $lineno: $msg"
}
trap 'failure ${LINENO} "$BASH_COMMAND"' ERR

cp -- "$file1" "$file2"
cp -- "$file3" "$file4"

Ini bekerja dengan menjebak ERR dan kemudian memanggil failure()fungsi dengan nomor baris + perintah bash saat ini yang dieksekusi.

Contoh

Di sini saya sudah tidak diurus apapun untuk membuat file, f1, f2, f3, atau f4. Ketika saya menjalankan skrip di atas:

$ ./yael.bash
cp: cannot stat f1’: No such file or directory
Failed at 17: cp -- "$file1" "$file2"

Gagal, melaporkan nomor baris plus perintah yang dieksekusi.

slm
sumber
14

Selain LINENOberisi nomor baris saat ini, ada array BASH_LINENOdan FUNCNAME(dan BASH_SOURCE) yang berisi nama fungsi dan nomor baris tempat mereka dipanggil.

Jadi Anda bisa melakukan sesuatu seperti ini:

#!/bin/bash

error() {
        printf "'%s' failed with exit code %d in function '%s' at line %d.\n" "${1-something}" "$?" "${FUNCNAME[1]}" "${BASH_LINENO[0]}"
}

foo() {
        ( exit   0 ) || error "this thing"
        ( exit 123 ) || error "that thing"
}

foo

Menjalankan itu akan mencetak

'that thing' failed with exit code 123 in function 'foo' at line 9.

Jika Anda menggunakan set -e, atau trap ... ERRuntuk secara otomatis mendeteksi kesalahan, perhatikan bahwa mereka memiliki beberapa peringatan. Ini juga lebih sulit untuk memasukkan deskripsi tentang apa yang dilakukan skrip pada saat itu (seperti yang Anda lakukan dalam contoh Anda), meskipun itu mungkin lebih bermanfaat bagi pengguna biasa daripada hanya nomor baris.

Lihat misalnya ini untuk masalah dengan set -edan lainnya:

ilkkachu
sumber
13

Bash memiliki variabel bawaan $LINENO yang digantikan oleh nomor baris saat ini ketika dalam sebuah pernyataan, jadi Anda bisa melakukannya

in_case_fail $? "at $LINENO: cp $file1 $file2"

Anda juga dapat mencoba menggunakan trap ... ERR yang berjalan saat perintah gagal (jika hasilnya tidak diuji). Misalnya:

trap 'rc=$?; echo "error code $rc at $LINENO"; exit $rc' ERR

Kemudian jika perintah seperti cp $file1 $file2gagal Anda akan mendapatkan pesan kesalahan dengan nomor baris dan keluar. Anda juga akan menemukan perintah dalam kesalahan dalam variabel $BASH_COMMAND(meskipun tidak ada pengalihan, dll.).

meuh
sumber