Mam skrypt powłoki o nazwie „teleport.sh” w następujący sposób:
if [ $1="1" ];
then
shift
mv "$@" ~/lab/Sun
elif [ $1="2" ];
then
shift
mv "$@" ~/lab/Moon
elif [ $1="3" ];
then
shift
mv "$@" ~/lab/Earth
fi
Kiedy wykonam:
sh teleport.sh 2 testfile
Zostało testfile
to przeniesione do ~/lab/Sun
katalogu, co mnie bardzo myli, ponieważ nie przekazałem 1 lub 1 do tego skryptu.
Co tu jest nie tak?
$var
,$(cmd)
a nawet`cmd`
[do których$(cmd)
należy preferować]). Są przypadki skrajne, w których nie musisz cytować, ale zawsze robienie tego nie zaszkodzi.$(cmd)
to podstawianie poleceń (w większości) to samo co`cmd`
. Zobacz mywiki.wooledge.org/CommandSubstitution i mywiki.wooledge.org/BashFAQ/082Odpowiedzi:
Używanie spacji rozwiązuje problem.
Chociaż jest to ładniejsze:
źródło
$1="1"
składnia w ogóle „działa” (dając prawdziwy wynik). W jaki sposób wbudowane[
/test
interpretuje to wyrażenie?test
widzi tylko jeden argument („wartość l”). Bez innych argumentów („operator” i „wartość r”) nie ma nic do przetestowania, więctest
po prostu mówi „ok, cóż, tak, dałeś mi coś i to musi być szczerze prawda”.$1="1"
jest$1
konkatenowany przez=1
Pierwszy oczywistą rzeczą jest należy podać spacji pomiędzy argumentami
[
,test
czy[[
:W Bash
[[ ]]
zaleca się używanie, ponieważ nie robi rzeczy niepotrzebnych dla wyrażeń warunkowych, takich jak dzielenie słów i rozwijanie nazw ścieżek. Cytowanie wokół podwójnych cudzysłowów również nie jest potrzebne.==
Można również użyć bardziej czytelnego operatora .Dodano Uwaga: Jeśli drugi argument zawiera również zmienne, powołując jest konieczne, ponieważ może to być przedmiotem wyszukiwania wzorca, jeśli zawiera rozpoznawalne znaki takie jak
*
,?
,[]
itp .. Jeśli rozszerzony globbing lub pasujące do wzorca jest włączonashopt -s extglob
, inne formy podoba@()
,!()
itp będą również rozpoznawane jako wzory. Zobacz Dopasowywanie wzorów .Z operatorami podobnymi
<
i>
może być to nadal konieczne, ponieważ kiedyś napotkałem błąd, w którym brak podania drugiego argumentu spowodował różne wyniki.Jeśli chodzi o pierwszy operand, nic nie ma zastosowania.
Rozważ również tę prostszą odmianę:
Lub skondensowane:
"${@:2}"
jest formą rozszerzenia podłańcucha lub rozszerzenia elementu tablicy, gdzie2
jest przesunięcie. To powoduje, że ekspansja rozpoczyna się od drugiej wartości. Dzięki temu możemy nie musieć korzystaćshift
.Dodane
--
zapobiegamv
rozpoznawaniu nazw plików rozpoczynających się od dash (-
) jako niepoprawne opcje.źródło
;&
zamiast;;
(tylko ksh, bash, zsh). Ale wtedybreak
nadal nie zapobiega upadkowi,break
wystarczy wyrwać się z pętli.if
ale[
polecenie.[[ $foo == $bar ]]
wykona dopasowanie wzorca, ale[[ $foo == "$bar" ]]
nie zrobi tego.Aby odpowiedzieć na pytanie, dlaczego tak się dzieje, takie zachowanie
[
akatest
jest udokumentowane w POSIX :Podajesz mu 1 argument,
2=1
który nie jest zerowy i dlategotest
kończy się sukcesem.Innych stanowisk (i shellcheck ) zwracają uwagę, jeśli chcesz porównać dla równości, byś zamiast musiał przejść 3 argumenty
2
,=
i1
.źródło
Chciałbym tylko polecić przenośną, ale także ładniejszą alternatywę. Bash nie jest uniwersalny (a jeśli nie potrzebujesz uniwersalnego, dlaczego piszesz skrypt powłoki?)
(Uwaga dla pedantów: tak, wiem, że cytaty
$action
wcase "$action" in
wierszu są niepotrzebne, ale uważam, że najlepiej jest je tam umieścić, aby przyszli czytelnicy nie musieli o tym pamiętać.)źródło
/bin/sh
skrypty, jeśli potrzebujesz; w przeciwnym razie pisz w języku skryptowym, który jest mniej straszny niż shell. Podstawowy interpreter Perla jest bardziej prawdopodobne, że będzie obecny w starszych, zastrzeżonych środowiskach i odciętych środowiskach wbudowanych niż Bash.