Koşullu boru hattı


39

Diyelim ki aşağıdaki boru hattım var:

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

Bazı şartlar altında ve cmd3arasına eklemek istiyorum . Cmd2 sonucunu geçici bir dosyaya kaydetmeden bir koşullu boru hattı oluşturmanın bir yolu var mı? Gibi bir şey düşünürdüm:cmd2cmd4

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

Yanıtlar:


36

Sadece olağan &&ve ||operatörler:

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

(Satır boru ile sona erdiğinde hiçbir arka eğik çizgi gerekmediğini unutmayın.)

Jonas'ın gözlemine göre güncelleyin .
Cmd3 sıfır olmayan çıkış koduyla sonlanabilir catve kalan girişi işlemek istemezseniz , mantığı ters çevirin:

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

Kullanışlı ipucu Noted!
invert


2
Mantığı tersine çevirmek yardımcı olmuyor. Eğer catsıfırdan farklı çıkış durumu (ortak bir SIGPIPE alma) ile döner, daha sonra cmd3işletilecek. Thor'un cevabında veya çalıştırmaktan kaçınan diğer çözümlerde / sonra / başkasında olsa daha iyi kullanın cat.
Stéphane Chazelas

bu yüzden kedi bir şeyleri birbirine bağlamak için kullanılabilir, "geçiş" akışı gibi
Alexander Mills

19

if/ else/ fiçalışır. Bourne benzeri herhangi bir kabuk varsayarak:

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

10

Şimdiye kadar verilen tüm cevaplar cmd3ile değiştirin cat. Ayrıca herhangi bir komutu çalıştırmaktan da kaçınabilirsiniz:

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

Bu POSIX’dir, ancak modda olmayan bir bashkomut dosyasında (ile başlayan bir komut dosyasında olduğu gibi ), takma adın genişletilmesini (veya ) ile etkinleştirmeniz gerekeceğini unutmayın .bashsh#! /path/to/bashshopt -s expand_aliasesset -o posix

Gereksiz bir komutu hala çalıştırmayan bir başka yaklaşım ise eval 'u kullanmaktır:

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

Veya:

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

Linux'ta (en azından) bunun yerine , verileri çekirdekler ve kullanıcı alanı arasında iki kez hareket ettirmekten kaçınan verileri iki boru arasında geçirmek için + yerine kullananları catkullanabilirsiniz .pv -qsplice()read()write()


9

İnsan eserinin kabul ettiği cevabının bir eki olarak : yanlış-yanlış-ya da bunun akarsu ile etkileşiminin farkında olun. Örneğin,

true && false || echo foo

çıktılar foo. Şaşırtmayan bir şekilde,

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

ve

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

her ikisi de çıktı baz. (Bunun echo foo | grep baryanlış olduğunu ve çıktısının olmadığını unutmayın ). Ancak,

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

hiçbir şey çıkarmaz. İstediğiniz bu olabilir veya olmayabilir.


Bunun burada ne kadar alakalı olduğunu göremiyorum. else(Ya da ||) değişmeden giriş tutmak boş bir işlem olarak davranmalıdır. Yani değiştirilmesi catile echodeğişir her şey kodunuzu alakasız yapma. “Ve-yanlış-ya da-getcha” ile ilgili olarak, boru hattına herhangi bir müdahale göremiyorum: pastebin.com/u6Jq0bZe
manatwork

5
@manatwork: Jonas işaret eden bir husus içinde, yani [[ "${DEFINED}" ]] && cmd3 || cat, cmd3ve catbirbirini dışlayan değil thenve elseaynı dallarını if, ama eğer cmd3başarısız, o zaman catda çalıştırılacaktır. (Tabii ki, cmd3her zaman başarılı olursa , ya da tüm girdiyi tüketirse, böylece işlemek stdiniçin hiçbir şey kalmazsa cat, o zaman farketmeyebilir.) Her ikisine de thenve elsedallara ihtiyacınız olursa , açık bir ifkomut kullanmak daha iyidir , &&ve ||.
musiphil

1
Açıklama için teşekkürler @ musiphil. Şimdi Jonas'ın argümanını anlıyorum.
Manat çalışması

4

Bash işlevleriyle çözdüğüm benzer bir durum vardı :

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

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

3

Alternatif bir olası çözüm:
sözde bir boru hattı oluşturmak için adlandırılmış boruları birleştirmek:

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

Gelecekte referans olarak, buraya balıkta koşullu borular aramak için geliyorsanız, işte böyle yaparsınız:

set -l flags "yes"
# ... 
echo "conditionalpipeline" | \
  if contains -- "yes" $flags 
    sed "s/lp/l p/"
  else
    cat
  end | tr '[:lower:]' '[:upper:]'
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.