Jaka jest najlepsza praktyka reprezentowania wartości logicznej w skrypcie powłoki?

15

Wiem, że w tym są wartości boolowskie bash, ale nigdzie ich nie widzę.

Chcę napisać opakowanie na niektóre często wyszukiwane informacje na moim komputerze, na przykład czy ten konkretny dysk USB jest włożony / zamontowany.

Jaka byłaby najlepsza praktyka, aby to osiągnąć?

  • Sznurek?

    drive_xyz_available=true
  • Liczba (0 dla wartości true, ≠ 0 dla wartości false)?

    drive_xyz_available=0    # evaluates to true
  • Funkcja?

    drive_xyz_available() { 
        if available_magic; then 
                return 0 
        else 
                return 1 
        fi
    }

Zastanawiam się głównie, czego mogą oczekiwać inni ludzie, którzy chcieliby korzystać z opakowania. Czy spodziewaliby się wartości logicznej, polecenia podobnego do zmiennej lub funkcji do wywołania?

Z punktu widzenia bezpieczeństwa uważam, że druga opcja jest najbezpieczniejsza, ale chciałbym usłyszeć twoje doświadczenia.

Minix
źródło
3
help true ; help false ; help exit
Costas
3
@Casas Czy miałbyś coś przeciwko opracowaniu?
Minix

Odpowiedzi:

2
bool(){ return "$((!${#1}))"; }

if bool "$var"
then : do true
else : do false

Po prostu ustaw zmienną na cokolwiek, ale nie na zero, aby powyższe działało, choć [ -n "$var" ]byłoby krótsze, jeśli nie tak oczywiste.

Zasadniczo, gdy skrypt interpretuje zmienną środowiskową jako prawdę lub fałsz, zinterpretuje dowolną wartość jako prawdę (i czasami używa tej wartości do skonfigurowania jakiejś opcji) lub wartość zerową jako fałsz.

Powyższe zwraca !notwartość logiczną len pierwszego argumentu - jeśli argument zawiera dowolną liczbę znaków innych niż 0, zwraca 0, w przeciwnym razie, jeśli nie ma żadnych znaków, zwraca 1. Jest to ten sam test, który można wykonać [ -n "$var" ], zasadniczo , ale po prostu otacza go małą funkcją o nazwie bool().

Tak zazwyczaj działa zmienna flagowa . Na przykład:

[ -d "$dir" ] || dir=

Tam, gdzie inne części skryptu potrzebują tylko jakiejkolwiek wartości, $diraby ocenić jej przydatność. Jest to również przydatne, gdy chodzi o zastępowanie parametrów - ponieważ parametry można rozszerzyć do wartości domyślnych, aby wypełnić je pustymi lub nieuzbrojonymi, ale w przeciwnym razie zostaną rozszerzone do wstępnie ustawionej wartości, takiej jak ...

for set in yes ''
do echo "${set:-unset or null}"
done

... który wydrukowałby ...

yes
unset or null

Oczywiście możliwe jest również odwrotne działanie, :+ale może to dać tylko domyślną wartość domyślną lub w ogóle nic, podczas gdy powyższa forma może dać wartość lub wartość domyślną.

I tak w odniesieniu do trzech opcji - każda może działać w zależności od tego, jak zdecydujesz się ją wdrożyć. Zwrotem funkcji jest autotest, ale jeśli zwrot ten wymaga zapisania z jakiegokolwiek powodu, należy go umieścić w zmiennej. Zależy to od przypadku użycia - czy wartość logiczna ma być oceniana raz, a typ wykonania? Jeśli tak, wykonaj tę funkcję, w przeciwnym razie prawdopodobnie konieczne będzie jedno z dwóch pozostałych.

mikeserv
źródło
1
Nie sądzę, że odpowiadasz na moje pytanie. Nie pytam, w jaki sposób można użyć booleanów w skrypcie powłoki, ale jaki jest najbardziej powszechny i ​​oczekiwany przez innego użytkownika. Jeśli moje pytanie jest niejasne, chętnie je zredaguję. Przydałoby się również krótkie wyjaśnienie tego, co robi twoja odpowiedź. Dziękuję Ci.
Minix,
@Minix Czy jest coś lepszego?
mikeserv
Generalnie przypisuję truelub falsedo zmiennej, którą możesz zrobićif $variable; then ...
wurtel
@wurtel - który opiera się na ustawieniach domyślnych $IFS- i jeśli wartość var ​​może zawierać dane wejściowe użytkownika, to również powodzenia. Jeśli wartość nie jest nieznana na początku, wówczas nie ma potrzeby jej testowania. Bezpieczniej możesz to zrobić if ${var:+":"} false; then, pracując z wartościami logicznymi null / not null. Ale to rzadko jest bardziej przydatne niż[ -n "$var" ] &&
mikeserv
Jeśli zacznę swój skrypt, variable=falsea następnie ustawię go na true zgodnie z dowolnym warunkiem (tak jak zrobiłbym to przy użyciu zmiennej w C), to nie będzie żadnego problemu, jaka jest twoja obsesja na punkcie zmieniania wartości IFS i losowych wartości zmiennych itp. ,
wurtel
4

W bashkażdej zmiennej jest zasadniczo ciąg (lub tablica lub funkcja, ale porozmawiajmy tutaj o zmiennych regularnych).

Warunki są analizowane na podstawie zwracanych wartości poleceń testowych - zwracana wartość nie jest zmienną, lecz stanem wyjścia. Podczas oceny if [ ... ]lub if [[ ]]lub if grep somethinglub coś podobnego, zwracana wartość 0 (nie łańcuch 0, ale stan exit 0 = sukces) oznacza prawdę, a reszta średnia false (tak, dokładnie odwrotnie od czego są wykorzystywane w skompilowanych języków programowania, ale ponieważ istnieje jeden sposób na sukces i wiele sposobów na niepowodzenie, a oczekiwanym rezultatem wykonania jest zwykle sukces, 0 jest używane jako najczęstszy domyślny wynik, jeśli nic nie pójdzie źle. Jest to bardzo przydatne, ponieważ każdy plik binarny może być użyty jako test - jeśli zawiedzie, jest fałszywy, w przeciwnym razie jest prawdziwy.

truea falseprogramy (zwykle zastępowane przez wbudowane) są po prostu przydatnymi małymi programami, które nic nie robią - nie truerobią nic i wychodzą z 0, podczas gdy falsepróbują nic nie robić i „kończą się niepowodzeniem”, wychodząc z 1. Brzmi bez sensu, ale jest bardzo przydatne do pisania skryptów.

To, jak przekazać prawdę, zależy od ciebie. Dosyć często używa się po prostu „y” lub „tak” dla prawdy i użycia if [ x"$variable" = x"yes" ](dołączono fikcyjny ciąg, xponieważ jeśli $variablema zerową długość, chroni to przed tworzeniem fałszywego polecenia, if [ = "yes" ]które nie analizuje). Przydatne może być również użycie pustego ciągu dla false i użycie go [ -z "$variable ]do sprawdzenia, czy ma zerową długość (lub -nżeby nie był zerowy).

W każdym razie dość rzadko trzeba przekazywać wartości boolowskie bash- o wiele bardziej powszechne jest po prostu exitniepowodzenie lub zwracanie użytecznego wyniku (lub zero, jeśli coś pójdzie nie tak i testowanie pustego ciągu), a większość przypadków może test na awarię bezpośrednio ze statusu wyjścia.


W twoim przypadku potrzebujesz funkcji, która będzie działać jak każde inne polecenie (dlatego zwróci 0 po sukcesie), więc twoja ostatnia opcja wydaje się właściwym wyborem.

Ponadto może nie być potrzebne returnoświadczenie. Jeśli funkcja jest wystarczająco prosta, możesz użyć faktu, że po prostu zwraca status ostatnio wykonanego polecenia w funkcji. Twoja funkcja może po prostu być

drive_xyz_available() {
   [ -e /dev/disk/by-uuid/whatever ]
}

jeśli testujesz istnienie węzła urządzenia (lub grep, /proc/mountsaby sprawdzić, czy jest on zamontowany?).

orion
źródło
To bardzo miłe podsumowanie, dziękuję za poświęcenie czasu na napisanie go. Czy mogę wywnioskować z twojego ostatniego akapitu, że rozważysz opcję drive_xyz_available()najczęstszą?
Minix
Czy możesz podać kilka przykładów typowego y = trueprzypadku? Z mojego doświadczenia wynika, że ​​większość skryptów opakowania sprawdza, czy nie ma wartości zerowej, aby uznać ją za prawdziwą - przynajmniej jeśli chodzi o interpretowane zmienne środowiskowe. W przeciwnym razie całkowicie ignorują samochody pancerne.
mikeserv
3
Atrapa ciągu iftestu nie jest konieczna, jeśli zmienna jest cytowana; if [ "$variable" = "yes" ]działa dobrze, nawet jeśli zmienna $ nie jest ustawiona.
Daniel Kullmann
-2

Zasadniczo każda liczba, która nie jest zerem, jest prawdą, a 0 jest fałszem. Powód zwrócenia wartości jako 0 dla pomyślnego zakończenia skryptu lub dowolnej innej liczby w celu identyfikacji różnego rodzaju błędów.

$? zwróci kod zakończenia poprzedniej komendy / pliku wykonywalnego, będący 0 sukcesem, a każdą inną liczbą zwrócony błąd.

Więc użyłbym tej metody dla true / false. if (( ! $? ));then OK;else NOOK;fi

YoMismo
źródło
Napiszę więc jedną dla ostatniej opcji. Dziękuję Ci.
Minix,
5
Liczba niezerowa jest w rzeczywistości fałszem, a zero jest prawdą. Wystarczy spojrzeć na dane wyjściowe true; echo $?i false; echo $?.
Ruslan
@Ruslan. Czy sprawdziłeś wyjście mojego polecenia? Chyba tego nie zrobiłeś, inaczej nie powiedziałbyś, co zrobiłeś. 0 jest fałszem, każda inna liczba jest prawdą, powód do zanegowania !wyniku, aby był PRAWDA. Wynik polecenia wynosi 0, gdy poprawnie się zakończy, co nie oznacza, że ​​0 jest prawdziwe. Słowo truemoże mieć wartość 0, ale 0 nigdy nie jest prawdą, jak ifpokazuje warunek.
YoMismo
To tak samo jak powiedzenie, że w C / C ++, 0to truedlatego, że if(!x){True();}else{False();}zadzwoni True()po x==0. Ale poprawna kontrola nie byłaby !x, ale raczej !!x.
Ruslan
Jesteś w błędzie. Nie mówię o tym, co i jaką kolejność ma cokolwiek piszesz po tym, po ifprostu stwierdzam, że tutaj (w bash, ksh, tsh, lub ....) jak w C / C ++ a 0 jest FAŁSZ, dowolny inna liczba to PRAWDA, jak można przeczytać w poniższym linku, początkowe implementacje języka C nie zapewniały typu logicznego, zdefiniowane jako ints, gdzie 0 to FAŁSZ, a 1 PRAWDA en.wikipedia.org/wiki/Boolean_data_type .
YoMismo,