web-dev-qa-db-fra.com

Pipeline conditionnelle

Dis que j'ai le pipeline suivant:

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

Dans certaines conditions, je voudrais ajouter un cmd3 entre cmd2 et cmd4. Existe-t-il un moyen de créer une sorte de pipeline conditionnel sans économiser le résultat de cmd2 dans un fichier temporaire? Je penserais à quelque chose comme:

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

Juste l'habituel && et || les opérateurs:

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

(Notez qu'aucune barre oblique inverse n'est nécessaire lorsque la ligne se termine par le tuyau.)

Mise à jour Selon - Observation de Jonas ' .
[.____] Si cmd3 peut terminer avec un code de sortie non nul et que vous ne voulez pas cat pour traiter l'entrée restante, inverser la logique:

cmd1 < input.txt |
cmd2 |
( [ -z "$DEFINED" ] && cat || cmd3 ) |
cmd4 |
cmd5 |
cmd6 |
(...) |
cmdN > result.txt
37
manatwork

if/else/fi fonctionne. En supposant que n'importe quel shell de Bourne:

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

Toutes les réponses données jusqu'à présent remplacent cmd3 Avec cat. Vous pouvez également éviter de courir n'importe quelle commande avec:

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

C'est POSIX, mais notez que si dans un script bash _ _ bash n'est pas dans sh- mode (comme avec un script commençant par #! /path/to/bash), Vous Il faut activer l'expansion d'alias avec shopt -s expand_aliases (ou set -o posix).

Une autre approche qui ne fonctionne toujours pas de commande inutile consiste à utiliser EVAL:

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

Ou:

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

Sur Linux (au moins), au lieu de cat, vous pouvez utiliser pv -q Utilisé splice() au lieu de read() + write() pour passer Les données entre les deux tuyaux qui évitent de disposer des données déplacées deux fois entre le noyau et l'espace utilisateur.

11
Stéphane Chazelas

Comme un addendum à réponse acceptée de Manatwork : Soyez conscient de la FALSE-FALS-OU GOTCHA et de son interaction avec des flux. Par exemple,

true && false || echo foo

sorties foo. Sans surprise,

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

et

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

la sortie baz. (Noter que echo foo | grep bar est faux et n'a pas de sortie). Cependant,

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

sorties rien. Cela peut ou peut ne pas être ce que vous voulez.

9
Jonas Kölker

J'ai eu une situation similaire que j'ai résolue avec Bash Fonctions:

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

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

Voici une solution alternative possible:
Concaténation de tuyaux nommés afin de créer un pseudo-pipeline:

dir="$(mktemp -d)"
addfifo() {
  stdin="$dir/$fifo_n"
  [ "$1" ] || mkfifo "$dir/$((fifo_n=fifo_n+1))"
  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"
4
Claudio

Pour une référence future, si vous arrivez ici à la recherche de tuyaux conditionnels dans poisson, voici comment vous le faites:

set -l flags "yes"
# ... 
echo "conditionalpipeline" | \
  if contains -- "yes" $flags 
    sed "s/lp/l p/"
  else
    cat
  end | tr '[:lower:]' '[:upper:]'
2
Jorge Bucaran