Tại sao, bash -x, phá vỡ kịch bản này?


13

Tôi có một kịch bản đo thời gian một số lệnh thực thi.

Nó cần lệnh "thực" time, nghĩa là một nhị phân chẳng hạn trong /usr/bin/time(vì bash-in-in không có -fcờ).

Dưới đây, một tập lệnh đơn giản hóa có thể được gỡ lỗi:

#!/bin/bash

TIMESEC=$(echo blah | ( /usr/bin/time -f %e grep blah >/dev/null ) 2>&1 | awk -F. '{print $1}')

echo ABC--$TIMESEC--DEF

if [ "$TIMESEC" -eq 0 ] ; then
   echo "we are here!"
fi

Lưu dưới dạng "test.sh" và thực thi:

$ bash test.sh
ABC--0--DEF
we are here!

Vì vậy, nó đã làm việc.

Bây giờ, hãy thử gỡ lỗi này bằng cách thêm "-x" vào dòng lệnh bash:

$ bash -x test.sh
++ echo blah
++ awk -F. '{print $1}'
+ TIMESEC='++ /usr/bin/time -f %e grep blah
0'
+ echo ABC--++ /usr/bin/time -f %e grep blah 0--DEF
ABC--++ /usr/bin/time -f %e grep blah 0--DEF
+ '[' '++ /usr/bin/time -f %e grep blah
0' -eq 0 ']'
test.sh: line 10: [: ++ /usr/bin/time -f %e grep blah
0: integer expression expected

Tại sao tập lệnh này bị hỏng khi chúng tôi sử dụng "-x" và hoạt động tốt mà không có nó?


1
Heh. Có vẻ như với -x, rằng $()cấu trúc đang nhận -xđầu ra được bao gồm như là một phần của giá trị kết quả của nó. Không biết đó là hành vi "mong đợi" hay lỗi ... Hoặc có thể đó là phần con ()bên trong thực sự mang lại -xđầu ra.
Jeff Y

Ngoài ra: Cài đặt BASH_XTRACEFDcho phép bạn chuyển hướng set -xđầu ra đến một nơi nào đó ít rắc rối hơn.
Charles Duffy

Câu trả lời:


21

Vấn đề là dòng này:

TIMESEC=$(echo blah | ( /usr/bin/time -f %e grep blah >/dev/null ) 2>&1 | awk -F. '{print $1}')

nơi bạn đang chuyển hướng lỗi tiêu chuẩn để phù hợp với đầu ra tiêu chuẩn. bash đang viết các thông báo theo dõi của nó đến lỗi tiêu chuẩn và (ví dụ) sử dụng tích hợp sẵn echocùng với các cấu trúc shell khác tất cả trong quy trình bash.

Nếu bạn thay đổi nó thành một cái gì đó như

TIMESEC=$(echo blah | sh -c "( /usr/bin/time -f %e grep blah >/dev/null )" 2>&1 | awk -F. '{print $1}')

nó sẽ giải quyết vấn đề đó và có lẽ là một sự thỏa hiệp chấp nhận được giữa truy tìm và làm việc:

++ awk -F. '{print $1}'
++ sh -c '( /usr/bin/time -f %e grep blah >/dev/null )'
++ echo blah
+ TIMESEC=0                 
+ echo ABC--0--DEF
ABC--0--DEF
+ '[' 0 -eq 0 ']'
+ echo 'we are here!'
we are here!

7

bạn cũng có thể thả subshell. rõ ràng đó là những chiếc vỏ được lồng vào nhau làm đảo lộn mọi thứ:

TIMESEC=$(
    echo blah |
    /usr/bin/time -f %e grep blah 2>&1 >/dev/null |
    awk -F. '{print $1}'
)

Nếu bạn làm:


...| ( subshell ) 2>pipe | ...

... bạn kết thúc với lớp con được khởi chạy để xử lý phần đó của đường ống lưu trữ lớp con bên trong. Bởi vì shell không chuyển hướng ngay cả đầu ra gỡ lỗi của lớp con bên trong (như nó cũng sẽ làm với bất kỳ loại {lệnh ghép nào khác ; } >redirectmà bạn có thể chọn sử dụng) cho phần của đường ống mà bạn kết hợp các luồng trộn. Nó phải làm theo thứ tự chuyển hướng.

Thay vào đó, nếu chỉ đơn giản đầu tiên bạn chuyển hướng chỉ đầu ra lỗi của các lệnh bạn đang cố gắng đánh giá, và để cho vỏ máy chủ của đầu ra làm cho nó để stderr, bạn không gió lên với cùng một vấn đề.

và vì thế...


... | command 2>pipe 1>/dev/null | ...

... Shell máy chủ có thể tự do tiếp tục viết stderr của nó ở nơi nó vừa ý trong khi chỉ chuyển hướng đầu ra của các lệnh mà nó gọi vào đường ống.


bash -x time.sh
+++ echo blah
+++ /usr/bin/time -f %e grep blah
+++ awk -F. '{print $1}'
++ TIMESEC=0
++ echo ABC--0--DEF
ABC--0--DEF
++ '[' 0 -eq 0 ']'
++ echo 'we are here!'
we are here!

Cho vẫn đề đó...


TIMESEC=$(
    echo blah |
    /usr/bin/time -f %e grep blah 2>&1 >/dev/null
)
printf %s\\n "ABC--${TIMESEC%%.*}--DEF"
if [ "${TIMESEC%%.*}" -eq 0 ] ; then
   echo "we are here!"
fi
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.