jeśli warunek na wielu liniach w powłoce bash

19

Mam funkcję powłoki bash, która pobiera argument i w razie potrzeby wykonuje coś na nim.

do_somthing() {
  if [need to do something on $1]
  then
    do it
    return 0
  else
    return 1
  fi
}

Chcę wywołać tę metodę z kilkoma argumentami i sprawdzić, czy przynajmniej jeden z nich się powiódł.

Próbowałem czegoś takiego:

if [ do_something "arg1" ||
     do_something "arg2" ||
     do_something "arg3" ]
then
  echo "OK"
else
  echo "NOT OK"
fi

Jaka będzie do tego poprawna składnia?
EDYCJA
Również - Chcę się upewnić, że nawet jeśli pierwszy warunek jest prawdziwy, wszystkie pozostałe warunki będą nadal oceniane.

Dzięki,

Itay
źródło
Zaktualizowałem swoją odpowiedź.
tectux
Dzięki, czy możesz podać przykład?
Itay
1
Do mojej odpowiedzi dodałem przykładowy kod.
tectux

Odpowiedzi:

7

Najpierw uruchom polecenia, a następnie sprawdź, czy przynajmniej jedno z nich się udało.

#!/bin/bash

success=0
do_something arg1 && success=1
do_something arg2 && success=1
do_something arg3 && success=1

if ((success)); then
    printf 'Success! At least one of the three commands succeeded\n'
fi
geirha
źródło
3
Jest to mylące, będę zadowolony, jeśli możesz podać wyjaśnienie, w jaki sposób instrukcje warunkowe działają w bash ..., do_something zwraca 0 po sukcesie, ale następnie aktualizujemy sukces do 1 ..., if ((success))ocenia na coś innego niż if success. Jestem zmieszany :)
Itay
2
@Itay ((...))to polecenie arytmetyczne. Jeśli wynik w środku jest niezerowy, zwraca true (0). Jeśli wynik w środku wynosi 0, zwraca false (1). „sukces” jest traktowany jako zmienna, która, jak się oczekuje, zawiera liczbę. Zobacz mywiki.wooledge.org/ArithmeticExpression
geirha
Dzięki, myślę, że część, która mnie pomyliła, to to, że 0 to prawda, a 1 to fałsz, zwykle jest odwrotnie :)
Itay
2
@Tak Tak, na początku może to być mylące. Bash różni się od języków ogólnego przeznaczenia tym, że jest „oparty na poleceniach”; nie wywołuje funkcji, nie ma klas i metod, uruchamia polecenia i testuje statusy wyjścia poleceń. O wiele łatwiej jest uruchamiać polecenia w bash niż w językach takich jak C, python, java, perl itp. Polecenia kończą się cyfrą 0, aby zasygnalizować sukces, a 1-255 dla różnych typów awarii. (Polecenie może zasadniczo odnieść sukces tylko w jeden sposób, ale nie powiedzie się z różnych powodów)
geirha
2
To rozwiązanie ma tę zaletę, że można zakomentować linie, które nie chcą, aby wyłączyć je tymczasowo
Josiah Yoder
31

Użyj ukośników odwrotnych.

if [ $(do_something "arg1") ] || \
   [ $(do_something "arg2") ] || \
   [ $(do_something "arg3") ]
then
  echo "OK"
else
  echo "NOT OK"
fi

EDYTOWAĆ

Ponadto - chcę się upewnić, że nawet jeśli pierwszy warunek jest spełniony, wszystkie pozostałe warunki będą nadal oceniane.

Nie jest to możliwe tylko w przypadku instrukcji if. Zamiast tego możesz użyć pętli for, która iteruje argumenty i ocenia je osobno. Coś jak:

do_something() {
  for x in "$@"
  do
    if [need to do something on $x]
    then
      do it
    else
      echo "no action required on $x"
    fi
  done
}
tectux
źródło
dzięki, próbowałem - dostaję błąd[: do_something: unary operator expected
Itay
Czy umieściłeś każdą ocenę w nawiasach? Więc [ expr1 ] || [ expr2 ]zamiast [ expr1 || expr2 ].
tectux
tak, zrobiłem: (...
Itay
Sądzę, że jest do_something "arg1"to interpretowane jako dwa argumenty, podczas gdy wyrażenie powinno być jednoargumentowe. Jakikolwiek sposób dokonywania do_something "arg1"oceny może być oceniany jako jeden argument?
Itay
1
Masz rację. Działa to też: if [ $(do_something "arg1") ].
tectux
5

Prawidłowa składnia to:

if  do_something "arg1" || \
    do_something "arg2" || \
    do_something "arg3"
then
  echo "OK"
else
  echo "NOT OK"
fi

\ służy do poinformowania powłoki, że polecenie jest kontynuowane w następnym wierszu.

EDYCJA : Myślę, że powinno to zrobić, co chcesz:

#!/bin/bash

do_something() {
  if [need to do something on $1]
  then
    do it
    echo "OK"
  else
    echo "NOT OK"
  fi
}

do_something "arg1"
do_something "arg2"
do_something "arg3"
Eric Carvalho
źródło
Dzięki, wyniki z błędem:[: too many arguments
Itay
@Itay Odpowiedź zaktualizowana.
Eric Carvalho,
Aktualizacja pytania - to jeszcze nie ostateczna odpowiedź :(
Itay
-1

Wolę:

if {
    do_something "arg1" ||
    do_something "arg2" ||
    do_something "arg3"
}; then
    echo "OK"
else
    echo "NOT OK"
fi
Toxiro
źródło
Twój skrypt w ogóle nie będzie działał: po pierwszym parametrze testowym wygenerowałby \nznak, więc twoja ifinstrukcja wygenerowałaby błędy, a do_somethingnie nazwy poleceń, więc więcej błędów
damadam
Dla mnie działa w Zsh i Bash, IF do_somethingjest oczywiście funkcją. do_somethingmusi być funkcją lub poleceniem, co jeszcze powinno być? Co więcej, nie widzę miejsca, w którym wygenerowałoby \npostać, chyba że po
powtórzeniu