Đường ống có điều kiện


39

Nói rằng tôi đã có đường ống sau:

cmd1 < input.txt |\
cmd2 |\
cmd4 |\
cmd5 |\
cmd6 |\
(...) |\
cmdN > result.txt

Trong một số điều kiện nhất định, tôi muốn thêm một cmd3giữa cmd2cmd4. Có cách nào để tạo một đường ống có điều kiện loại mà không lưu kết quả của cmd2 vào một tệp tạm thời không? Tôi sẽ nghĩ về một cái gì đó như:

cmd1 < input.txt |\
cmd2 |\
(${DEFINED}? cmd3 : cat ) |\
cmd4 |\
cmd5 |\
cmd6 |\
(...) |\
cmdN > result.txt

Câu trả lời:


36

Chỉ là thông thường &&và các ||nhà khai thác:

cmd1 < input.txt |
cmd2 |
( [[ "${DEFINED}" ]] && cmd3 || cat ) |
cmd4 |
cmd5 |
cmd6 |
(...) |
cmdN > result.txt

(Lưu ý rằng không cần dấu gạch chéo ngược khi đường kết thúc bằng đường ống.)

Cập nhật theo quan sát của Jonas .
Nếu cmd3 có thể chấm dứt với mã thoát khác không và bạn không muốn catxử lý đầu vào còn lại, hãy đảo ngược logic:

cmd1 < input.txt |
cmd2 |
( [[ ! "${DEFINED}" ]] && cat || cmd3 ) |
cmd4 |
cmd5 |
cmd6 |
(...) |
cmdN > result.txt

Mẹo tiện dụng Lưu ý!
đảo ngược

6
Hãy nhận biết về gotchas: unix.stackexchange.com/a/39043/19055
Jonas Kölker

2
Đảo ngược logic không giúp được gì. Nếu cattrả về với trạng thái thoát khác không (phổ biến khi nhận SIGPIPE), thì cmd3sẽ được chạy. Sử dụng tốt hơn nếu / thì / khác như trong câu trả lời của Thor hoặc các giải pháp khác tránh chạy cat.
Stéphane Chazelas

vì vậy mèo có thể được sử dụng để kết nối mọi thứ, giống như một luồng "thông qua"
Alexander Mills

19

if/ else/ ficông trình. Giả sử bất kỳ vỏ giống như Bourne:

cmd1 < input.txt |
cmd2 |
if [ -n "$DEFINED" ]; then cmd3; else cat; fi |
cmd4 |
cmd5 |
cmd6 |
(...) |
cmdN > result.txt

10

Tất cả các câu trả lời cho đến nay thay thế cmd3bằng cat. Bạn cũng có thể tránh chạy bất kỳ lệnh nào với:

if [ -n "$DEFINE" ]; then
  alias maybe_cmd3='cmd3 |'
else
  alias maybe_cmd3=''
fi
cmd1 |
cmd2 |
maybe_cmd3
cmd4 |
... |
cmdN > result.txt

Đó là POSIX, nhưng lưu ý rằng nếu trong bashtập lệnh bashkhông ở shchế độ (giống như tập lệnh bắt đầu #! /path/to/bash), bạn sẽ cần bật mở rộng bí danh bằng shopt -s expand_aliases(hoặc set -o posix).

Một cách tiếp cận khác vẫn không chạy bất kỳ lệnh không cần thiết nào là sử dụng eval:

if [ -n "$DEFINE" ]; then
  maybe_cmd3='cmd3 |'
else
  maybe_cmd3=''
fi
eval "
  cmd1 |
  cmd2 |
  $maybe_cmd3
  cmd4 |
  ... |
  cmdN > result.txt"

Hoặc là:

eval "
  cmd1 |
  cmd2 |
  ${DEFINE:+cmd3 |}
  cmd4 |
  ... |
  cmdN > result.txt"

Trên Linux (ít nhất), thay vì cat, bạn có thể sử dụng sử pv -qdụng splice()thay vì read()+ write()để truyền dữ liệu qua lại giữa hai ống dẫn để tránh việc dữ liệu được di chuyển hai lần giữa kernel và không gian người dùng.


9

Là một phụ lục cho câu trả lời được chấp nhận của manatwork : hãy nhận biết và sai - hoặc gotcha và sự tương tác của nó với các luồng. Ví dụ,

true && false || echo foo

đầu ra foo. Không đáng ngạc nhiên,

true && (echo foo | grep bar) || echo baz

echo foo | (true && grep bar || echo baz)

cả hai đầu ra baz. (Lưu ý đó echo foo | grep barlà sai và không có đầu ra). Tuy nhiên,

echo foo | (true && grep bar || sed -e abaz)

đầu ra không có gì. Điều này có thể hoặc không thể là những gì bạn muốn.


Tôi không thể thấy điều này có liên quan ở đây. Các else(hoặc ||) nên đóng vai trò là một hoạt động vô giữ đầu vào không thay đổi. Vì vậy, thay thế catbằng echothay đổi mọi thứ làm cho mã của bạn không liên quan. Liên quan đến các ứng dụng và sai-hoặc gotcha, tôi thấy không có sự can thiệp nào với đường ống: pastebin.com/u6Jq0bZe
manatwork

5
@manatwork: Một điều Jonas đang chỉ ra là, trong [[ "${DEFINED}" ]] && cmd3 || cat, cmd3catkhông phải là loại trừ lẫn nhau thenelsecác nhánh giống nhau if, nhưng nếu cmd3thất bại, thì catcũng sẽ bị xử tử. (Tất nhiên, nếu cmd3lúc nào cũng thành công, hoặc nếu nó tiêu thụ tất cả các đầu vào nên không có gì còn lại trong stdincho cattới quá trình, thì nó có thể không quan trọng). Nếu bạn cần cả hai thenelsecác chi nhánh, nó tốt hơn để sử dụng một rõ ràng iflệnh, không &&||.
musiphil

1
Cảm ơn bạn đã giải thích, @musiphil. Bây giờ tôi hiểu lập luận của Jonas.
manatwork

4

Tôi đã có một tình huống tương tự mà tôi đã giải quyết với các hàm bash :

if ...; then
  my_cmd3() { cmd3; }
else
  my_cmd3() { cat; }
if

cmd1 < input.txt |
cmd2 |
my_cmd3 |
cmd4 |
cmd5 |
cmd6 |
(...) |
cmdN > result.txt

3

Đây là một giải pháp khả thi khác:
nối các ống có tên để tạo đường ống giả:

dir="$(mktemp -d)"
fifo_n='0'
function addfifo {
 stdin="$dir/$fifo_n"
((fifo_n++))
[ "$1" ] || mkfifo "$dir/$fifo_n"
stdout="$dir/$fifo_n"; }

addfifo; echo "The slow pink fox crawl under the brilliant dog" >"$stdout" &

# Restoring the famous line: "The quick brown fox jumps over the lazy dog"
# just replace 'true' with 'false' in any of the 5 following lines and the pseudo-pipeline will still work
true && { addfifo; sed 's/brilliant/lazy/' <"$stdin" >"$stdout" & }
true && { addfifo; sed 's/under/over/'     <"$stdin" >"$stdout" & }
true && { addfifo; sed 's/crawl/jumps/'    <"$stdin" >"$stdout" & }
true && { addfifo; sed 's/pink/brown/'     <"$stdin" >"$stdout" & }
true && { addfifo; sed 's/slow/quick/'     <"$stdin" >"$stdout" & }

addfifo -last; cat <"$stdin"

wait; rm -r "$dir"; exit 0

2

Để tham khảo trong tương lai, nếu bạn đến đây để tìm kiếm các ống có điều kiện trong , đây là cách bạn làm điều đó:

set -l flags "yes"
# ... 
echo "conditionalpipeline" | \
  if contains -- "yes" $flags 
    sed "s/lp/l p/"
  else
    cat
  end | tr '[:lower:]' '[:upper:]'
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.