Làm thế nào tôi có thể chặn đầu ra chỉ khi lệnh thành công?


22

Tôi muốn đơn giản hóa đầu ra của một tập lệnh bằng cách triệt tiêu đầu ra của các lệnh thứ cấp thường thành công.

Tuy nhiên, sử dụng -qtrên chúng ẩn đầu ra khi chúng thỉnh thoảng bị lỗi, vì vậy tôi không có cách nào để hiểu lỗi. Ngoài ra, các lệnh này đăng nhập đầu ra của họ trên stderr.

Có cách nào để triệt tiêu đầu ra của lệnh chỉ khi nó thành công ?

Ví dụ (nhưng không giới hạn) một cái gì đó như thế này:

mycommand | fingerscrossed

Nếu mọi việc suôn sẻ, hãy fingerscrossedbắt đầu ra và loại bỏ nó. Khác nó lặp lại nó đến đầu ra tiêu chuẩn hoặc lỗi (bất cứ điều gì).

Câu trả lời:


36

moreutils' chronicLệnh thực hiện điều đó:

chronic mycommand

sẽ nuốt mycommandđầu ra, trừ khi nó thất bại, trong trường hợp đó đầu ra được hiển thị.


1
Cảm ơn bạn. Tôi cho rằng nó không được cài đặt theo mặc định trên hầu hết các hệ điều hành Unix?
Matthieu Napoli

1
Có lẽ là không, mặc dù nó được đóng gói rộng rãi nên dễ cài đặt.
Stephen Kitt

1
Debian có nó trong gói moreutils. Dù sao cũng tốt cho tôi :)
Tom Zych

6
Lưu ý rằng nó lưu trữ toàn bộ đầu ra trong bộ nhớ.
Stéphane Chazelas

1
@ StéphaneChazelas đó có lẽ là cách duy nhất để thực hiện một cái gì đó như thế này, đầu ra cần được lưu trữ trong khi lệnh đang chạy trong trường hợp cần thiết.
Centimane

11
### do this bit once at the top of your script
divert=
exec 3<>"${divert:=$(mktmp)}" 4<>/dev/null
rm -- "$divert"; unset divert
### then do this bit as often as needed
command >&3 2>&3
cat <&3 >&"$(((RTN=$?)?2:4))"

Điều đó có lẽ nên làm các mẹo. Nó sẽ đệm đầu ra của mỗi commandtệp thành một tệp tạm thời bị xóa và sau đó hút đầu ra của nó vào một trong hai /dev/nullhoặc stderr tùy thuộc vào trạng thái trả về của nó có bằng không hay không. Bởi vì tệp tạm thời bị xóa trước thời gian, nó không thể được đọc bởi bất kỳ quy trình nào ngoài trình bao hiện tại và các phần tử con của nó trên bộ mô tả tệp của nó (chặn những /proc/$pid/fdkẻ rình mò lén lút với các quyền thích hợp) và nó không yêu cầu dọn dẹp khi bạn đi qua.

Có lẽ một giải pháp thuận tiện hơn trên các hệ thống linux:

divert(){
    "$@" >&3 2>&3 ||
    eval "cat <&3
          return $?"
}   3<<"" 3<>/dev/fd/3

... trong hầu hết các shell, hoạt động giống như các shell khác, ngoại trừ việc bạn có thể gọi nó như : divert some simple-command with args. Cẩn thận với lệnh sản lượng cao trong "$@", dù cho dash, yashhoặc một số tiện ích khác mà làm ở đây-tài liệu với ống - Tôi nghĩ rằng nó có thể là có thể trong những vỏ để điền vào bộ đệm ống (với mặc định khoảng 128kb trên linuxes) và vì thế bế tắc . Đó không phải là một lo lắng cho ksh, mksh, bash, zsh, hoặc Bourne shell, mặc dù - tất cả những người làm về cơ bản được điều tương tự như tôi đã làm một cách rõ ràng ở trên với exec.


9

Thông thường trong trường hợp có lỗi, lệnh sẽ xuất các thông báo để stderrbạn thực hiện nhiệm vụ, bạn chỉ có thể thay thếstdout

mycommand > /dev/null


Cảm ơn bạn, nhưng như tôi đã nói trong câu hỏi, các lệnh của tôi ghi lại tất cả đầu ra trên stderr(vì vậy nó không có hiệu lực).
Matthieu Napoli

4

Để làm cho mãn tính của riêng bạn

my_chronic() {
  tmp=$(mktemp) || return # this will be the temp file w/ the output
  "$@"  > "$tmp" 2>&1 # this should run the command, respecting all arguments
  ret=$?
  [ "$ret" -eq 0 ] || cat "$tmp"  # if $? (the return of the last run command) is not zero, cat the temp file
  rm -f "$tmp"
  return "$ret" # return the exit status of the command
}

3

Tôi làm một cái gì đó như thế này trong makefiles của tôi:

if (mycommand) &> mycommand.log; then 
  echo success 
else 
  c=$?; 
  echo;echo -e "Bad result from previous command, see mycommand.log for more details";echo;
  command_to_run_on_fail
  (exit $c)
fi

Thích nghi với hoàn cảnh của bạn, bạn có thể làm một cái gì đó như thế này:

if ! (mycommand) &> mycommand.log; then 
  c=$?; 
  cat mycommand.log
  rm mycommand.log
  (exit $c)
fi

Vì vậy, "nếu" chạy lệnh và chuyển đầu ra sang mycommand.log. Nếu bạn cần bắt stdout vs stdout vs any, bạn có thể cần thay đổi lệnh pipe '&>' thành '>'. Nếu lệnh không thành công thì bắt mã lỗi, in ra nội dung của mycommand.log, xóa mycommand.log và cuối cùng trả về với mã lỗi ban đầu.

Nếu không có (exit $ c), bạn sẽ trả về với mã thoát khớp với lệnh 'rm' được trả về.

Cuối cùng, nếu bạn muốn có một lớp lót, một cái gì đó như thế này sẽ hoạt động.

mycommand &> mycommand.log || cat mycommand.log; rm mycommand.log

2
Bạn có thực sự bọc các lệnh của bạn / vv. trong (...)như thế? Bởi vì nó không làm gì hữu ích cho bạn ngoài việc sinh ra các lớp vỏ phụ.
Etan Reisner

@EtanReisner (exit $c)đang cài đặt $?, đây là điều bạn không thể làm khác được. if ! (mycommand) &>xcó ý nghĩa với chuyển hướng nếu lệnh sử dụng ví dụ timehoặc sẽ đưa ra lỗi shell.
Michael Homer

@MichaelHomer - đối với những thứ đó, có những { ; }đường cong ... mặc dù exitcó một chút khó khăn ở đó, phải thừa nhận.
mikeerv

Trong một đoạn trích thực hiện nếu bạn đang cố thoát ra với lưu trước $?đó thì bạn chỉ có thể sử dụng exit $cnhưng có, trong các trường hợp khác (exit $?)có giá trị (mặc dù rret() { return $1; }nói chung một hàm shell sẽ tốt hơn). Subshell cho các lệnh vẫn không thực sự mặc dù như mikeerv đã chỉ ra.
Etan Reisner

3

Tôi chỉ tìm thấy câu trả lời đơn giản hơn nhiều cho câu hỏi khác này :

output=`mycommand 2>&1` || echo $output

Hoạt động như một lá bùa!


Lưu ý: đối với trường hợp sử dụng của tôi, đây là một giải pháp đơn giản hơn nhiều (tránh cài đặt thêm nội dung trên tất cả các máy chủ CI) vì vậy tôi đã chọn đánh dấu cái này là chấp nhận. YMMV.
Matthieu Napoli

Lưu ý rằng nếu bạn sử dụng set -o xtrace trong tập lệnh shell của mình thì tất cả đầu ra sẽ lại ở đó như là một phần của việc ghi nhật ký chi tiết của đầu ra gán = =::). Trong trường hợp đó có lẽ tốt hơn để sử dụng mãn tính.
Jan-Philip Gehrcke
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.