Bỏ qua dấu vết thực thi bash (set -x) từ bên ngoài tập lệnh


17

Tôi đã cố gắng tìm câu trả lời cho câu hỏi này, nhưng không có may mắn cho đến nay:

Tôi có một tập lệnh chạy một số tập lệnh khác và nhiều tập lệnh khác có "set -x" trong đó, làm cho chúng in mọi lệnh chúng thực thi. Tôi muốn loại bỏ điều đó nhưng giữ lại thông tin nếu bất kỳ đoạn script nào gửi thông báo lỗi tới stderr.

Vì vậy, tôi không thể đơn giản viết ./script 2>/dev/null

Ngoài ra, tôi không có đặc quyền để chỉnh sửa các tập lệnh khác, vì vậy tôi không thể thay đổi tùy chọn cài đặt theo cách thủ công.

Tôi đã suy nghĩ về việc ghi nhật ký mọi thứ từ stderr vào tệp riêng biệt và lọc ra các lệnh truy tìm, nhưng có lẽ có một cách đơn giản hơn?


1
./script 2>some_file
Satō Katsura

Câu trả lời:


25

Với bash4.1 trở lên, bạn có thể làm

BASH_XTRACEFD=7 ./script.bash 7> /dev/null

(cũng hoạt động khi bashđược gọi là sh).

Về cơ bản, chúng tôi đang yêu bashcầu xuất đầu xtracera trên bộ mô tả tệp 7 thay vì mặc định là 2 và chuyển hướng mô tả tệp đó sang /dev/null. Số fd là tùy ý. Sử dụng fd trên 2 mà không được sử dụng trong tập lệnh của bạn. Nếu shell bạn đang nhập lệnh này là bashhoặc yash, bạn thậm chí có thể sử dụng một số trên 9 (mặc dù bạn có thể gặp vấn đề nếu bộ mô tả tệp được sử dụng bên trong shell).

Nếu trình bao bạn đang gọi bashtập lệnh đó là zsh, bạn cũng có thể thực hiện:

(export BASH_XTRACEFD; ./script.bash {BASH_XTRACEFD}> /dev/null)

để biến được tự động gán fd miễn phí đầu tiên trên 9.

Đối với các phiên bản cũ hơn bash, một tùy chọn khác, nếu xtraceđược bật bằng set -x(trái ngược với #! /bin/bash -xhoặc set -o xtrace) sẽ xác định lại setlà một hàm được xuất mà không có gì khi được thông qua -x(mặc dù điều đó sẽ phá vỡ tập lệnh nếu nó (hoặc bất kỳ bashtập lệnh nào khác mà nó gọi) được sử dụng setđể đặt các tham số vị trí).

Giống:

set()
  case $1 in
    (-x) return 0;;
    (-[!-]|"") builtin set "$@";;
    (*) echo >&2 That was a bad idea, try something else; builtin set "$@";;
  esac

export -f set
./script.bash

Một tùy chọn khác là thêm bẫy DEBUG trong $BASH_ENVtệp set +xtrước mỗi lệnh.

echo 'trap "{ set +x; } 2>/dev/null" DEBUG' > ~/.no-xtrace
BASH_ENV=~/.no-xtrace ./script.bash

Điều đó sẽ không hoạt động khi set -xđược thực hiện trong một vỏ phụ.

Như @ilkkachu đã nói, miễn là bạn có quyền ghi vào bất kỳ thư mục nào trên hệ thống tập tin, ít nhất bạn sẽ có thể tạo một bản sao của tập lệnh và chỉnh sửa nó.

Nếu không có nơi nào bạn có thể viết một bản sao của tập lệnh hoặc nếu nó không thuận tiện để tạo và chỉnh sửa một bản sao mới mỗi khi có bản cập nhật cho tập lệnh gốc, bạn vẫn có thể thực hiện:

 bash <(sed 's/set -x/set +x/g' ./script.bash)

Điều đó (và cách tiếp cận sao chép) có thể không hoạt động chính xác nếu tập lệnh thực hiện bất kỳ thứ gì ưa thích với $0hoặc các biến đặc biệt như $BASH_SOURCE(chẳng hạn như tìm kiếm các tệp có liên quan đến vị trí của tập lệnh), vì vậy bạn có thể cần phải chỉnh sửa thêm như thay thế $0bằng đường dẫn của tập lệnh ...


Câu trả lời đầu tiên của bạn là chính xác những gì tôi cần, sạch sẽ và thanh lịch. Bạn có thể giải thích một chút làm thế nào nó hoạt động? Tại sao là số 7? Những con số khác có thể được sử dụng để làm gì? Cảm ơn.
con người

@human, xem chỉnh sửa
Stéphane Chazelas 17/03/2017

1
Thủ {BASH_XTRACEFD}>thuật hoạt động trong bash4.1 trở lên là tốt.
chepner 17/03/2017

@chepner, vâng, tính năng này đã được thêm vào zsh, ksh93 và bash cùng lúc theo đề xuất của nhà phát triển zsh. Nhưng, ở đây nó không làm việc cho ksh93hoặc bashtrong đó các biến không được thông qua trong môi trường của lệnh (so sánh <shell> -c 'export fd; printenv fd {fd}> /dev/null'trong zsh, bashksh93). Bạn có thể làm cho nó hoạt động trong ksh93/ bashbằng cách thực hiện theo hai bước hoặc có thể sử dụng eval, nhưng đối với bash, điều đó sẽ có tác dụng phụ nếu tùy chọn xtrace được bật.
Stéphane Chazelas 17/03/2017

5

Vì chúng là các tập lệnh, bạn có thể tạo các bản sao của chúng và chỉnh sửa chúng.

Ngoài ra, việc lọc đầu ra có vẻ đơn giản, bạn rõ ràng có thể đặt PS4thành một thứ gì đó khác thường hơn một điểm cộng, để làm cho việc lọc dễ dàng hơn:

PS4="%%%%" bash script.sh 2>&1 | grep -ve '^%%%%'

(tất nhiên điều đó sẽ đánh sập stdout và stdin, nhưng đường ống chỉ stderr trong Bash có một chút lông, vì vậy tôi sẽ bỏ qua điều đó)


1
Đường ống chỉ stderr là dễ dàng : PS4="%%%%" bash script.sh 2> >(grep -ve '^%%%%').
Patrick

4
@Patrick, làm điều đó bashcó vấn đề vì nó grepđược chạy không đồng bộ (bash không chờ nó, vì vậy nó có thể (và thường làm) xuất ra những thứ sau khi lệnh tiếp theo trong tập lệnh được bắt đầu).
Stéphane Chazelas 17/03/2017
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.