Cách nhận cả PIPESTATUS và đầu ra trong tập lệnh bash


9

Tôi đang cố gắng lấy ngày sửa đổi cuối cùng của tệp bằng lệnh này

TM_LOCAL=`ls -l --time-style=long-iso ~/.vimrc | awk '{ print $6" "$7 }'`

TM_LOCAL có giá trị như "2012-05-16 23:18" sau khi thực hiện dòng này

Tôi cũng muốn kiểm tra PIPESTATUS để xem có lỗi không. Ví dụ: nếu tệp không tồn tại, lstrả về 2. Nhưng $?có giá trị 0 vì nó có giá trị trả về là awk.

Nếu tôi chạy lệnh này một mình, tôi có thể kiểm tra giá trị trả về của ls bằng cách xem ${PIPESTATUS[0]}

ls -l --time-style=long-iso ~/.vimrc | awk '{ print $6" "$7 }'

Nhưng $PIPESTATUSkhông hoạt động như tôi mong đợi nếu tôi gán đầu ra cho một biến như trong ví dụ đầu tiên. Trong trường hợp này, $PIPESTATUSmảng chỉ có 1 phần tử giống như$?

Vì vậy, câu hỏi là, làm thế nào tôi có thể nhận được cả hai $PIPESTATUSvà gán đầu ra cho một biến cùng một lúc?

Câu trả lời:


8

Bạn có thể làm điều này:

TM_LOCAL=$(ls -l --time-style=long-iso ~/.vimrc | \
             awk '{ print $6" "$7 }' ; exit ${PIPESTATUS[0]} )

Sau đó $?sẽ là mã trả về từ ls. Điều này không hoạt động nếu bạn cần mã trả về từ nhiều hơn một trong các bộ phận ống (nhưng bạn có thể tách đường ống nếu đầu ra không quá lớn, như ở đây).

Đây là một cách khá tốn kém để có được PIPESTATUSmảng đầy đủ và đầu ra. Không thanh lịch lắm, nhưng không tìm thấy gì khác:

result=$(echo -e "a\nb\nc" | \
          ( cat ; exit 1 ) | \
          ( cat ; exit 42 ) ; echo ${PIPESTATUS[@]})
output=$(head -n -1 <<< "$result")
status=($(tail -n 1 <<< "$result"))
echo "Output:"
echo "$output"
echo "Results:"
echo "${status[@]}"

Cung cấp cho:

Output:
a
b
c
Results:
0 1 42

Điều này hoạt động trong trường hợp của tôi, nhưng tôi vẫn tò mò về việc liệu có cách nào để có được mảng pipestatus hoàn chỉnh và đầu ra không.
Mustafa Serdar Şanlı

3

Sử dụng set -o pipefailtrong bashđể lấy mã lối ra phải nhất khác không trong một chuỗi lệnh đường ống như $?. Từ man bash:

Nếu được đặt, giá trị trả về của đường ống là giá trị của lệnh cuối cùng (ngoài cùng bên phải) để thoát với trạng thái khác không hoặc bằng 0 nếu tất cả các lệnh trong thoát đường ống thành công. Tùy chọn này được tắt theo mặc định.

Sau đó, bạn có thể chỉ cần truy cập $?. Sử dụng set +o pipefailđể vô hiệu hóa một lần nữa.


2

Tôi giả sử vấn đề ở đây là PIPESTATUS sẽ biến mất hoàn toàn ngay khi bạn thực thi một lệnh. Bạn có thể nhận được mảng PIPESTATUS hoàn chỉnh trong bash phiên bản 2 trở lên theo cách này:

declare -a status
status=(${PIPESTATUS[@]})

Sau đó truy cập ${status[0]}, ${status[1]}vv


2

Vấn đề chính với "những gì bạn mong đợi" là một lệnh trong backquote được thực thi trong một lớp con; $PIPESTATUStồn tại ở đó và trạng thái được trả về từ nó tuân theo các quy tắc giống như khi bạn thực thi một tập lệnh thực thi (hoặc shell script). Trạng thái của lệnh backquote là awktrạng thái ngoài cùng ( ).

Để thực hiện những gì @ Daniel Beck đã nói, hãy đặt pipefailtùy chọn trong phần con:

TM_LOCAL=`set -o pipefail; ls -l --time-style=long-iso ~/.vimrc | awk '{ print $6" "$7 }'` bây giờ trạng thái được lưu trữ $?sau đó sẽ là trạng thái của ls(nếu khác không).

Tuy nhiên, tôi nghĩ rằng một if [ -f ~/.vimrc ];bài kiểm tra ... rõ ràng sẽ dễ đọc hơn.

Bạn không thể nhận đầu ra thành một biến và được PIPESTATUStrả về mà không có tệp tạm thời cho tệp trước hoặc sắp xếp thứ tự thành chuỗi.


0

Tôi chỉ muốn gửi email fron cron nếu trạng thái thoát không bằng không

Mẹo nhỏ là để có được stdin cho phần cuối của đường ống, bạn cần đặt nó vào một khung con - nhưng điều đó dường như che giấu giá trị PIPESTATUS ...

kiểm tra cron phun ra một số đầu ra và thoát với 1 hoặc 0 ..

./testcron | (test ${PIPESTATUS[0]} -ne 0 || mail -s "testcron output" paul)

CẬP NHẬT: PIPESTATUS không hiển thị cho đến khi lệnh đường ống đang xử lý


0

Một tùy chọn là kiểm tra sự tồn tại của tệp của bạn trước khi nhận được thời gian sửa đổi với lệnh gọi đến stat. Vì stattrả về một chút nhiều hơn bạn muốn trong dấu thời gian, bạn có thể cắt nó bằng cách mở rộng tham số.

Với GNU stat(ví dụ trên Linux), bạn có thể chạy:

[[ -f ~/.vimrc ]] && TM_LOCAL=$(stat -c '%y' ~/.vimrc 2>/dev/null)
TM_LOCAL=${TM_LOCAL%:*}  # Safe to do, even if stat fails

Trên Mac OS X và các hệ thống BSD khác, statcú pháp khác nhau và có thể chỉ định định dạng thời gian:

[[ -f ~/.vimrc ]] && TM_LOCAL=$(stat -f '%Sm' -t '%Y-%m-%d %H:%M' ~/.vimrc 2>/dev/null)

Hiện tại câu trả lời GNU là gì, bạn nói thay đổi $TM_LOCALlà an toàn. Nó chỉ an toàn nếu bạn dự kiến ​​nó không có giá trị trước. Nói giá trị trước đây 2020-02-27 17:14và không có ~/.vimrctập tin. Sau đó bạn sẽ có 2020-02-27 17. Do đó, tôi sẽ xâu chuỗi hai dòng đó lại với nhau bằng một bổ sung &&hoặc (tốt nhất là vì điều đó không dễ đọc) sử dụng một khổ ifthơ.
Adam Katz
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.