Shell Script - błąd składni w pobliżu nieoczekiwanego tokena `else '

15

Z poniższego skryptu powłoki, dlaczego otrzymuję błędy

syntax error near unexpected token `else'

Skrypt powłoki

echo "please enter username"
read user_name
echo "please enter password"
read -s pass
echo ${ORACLE_SID}
SID=${ORACLE_SID}
if ["${ORACLE_SID}" != 'Test'] then
sqlplus -s -l $USER_NAME/$PASS@$SID <<EOF
copy from scott/tiger@orcl insert EMP using select * from EMP
exit
EOF
else
echo "Cannot copy"
fi
Jakub
źródło
możesz edytować wiersz „kopiuj z ...”, ponieważ może on obecnie pokazywać coś, czego nie chcesz pokazywać. (Mam jednak nadzieję, że są to już zmodyfikowane informacje, ponieważ byłyby bardzo słabe pod względem bezpieczeństwa)
Olivier Dulac
1
@OlivierDulac Jeśli w tym wierszu mowa jest o nazwie użytkownika i haśle, są one znane wszystkim użytkownikom bazy danych Oracle. Jest powszechny i ​​znany od początku istnienia bazy danych Oracle.
Jåcob,
@OlivierDulac Nie ma za co, trochę informacji na temat tego dba-oracle.com/t_scott_tiger.htm
Jåcob

Odpowiedzi:

25

Musisz zakończyć warunek w iften sposób:

if [ "${ORACLE_SID}" != 'Test' ]; then

lub tak:

if [ "${ORACLE_SID}" != 'Test' ]
then

Uwaga: musisz także wstawiać spacje [przed i przed ].

Powodem ;lub podziałem ifwiersza jest to, że część warunku instrukcji jest tylko poleceniem. Dokładnie każde polecenie dowolnej długości. Powłoka wykonuje to polecenie, sprawdza status wyjścia polecenia, a następnie decyduje, czy wykonać thenczęść, czy elseczęść.

Ponieważ polecenie może mieć dowolną długość, musi istnieć znacznik do oznaczenia końca części warunku. To jest ;lub nowa linia, po której następuje then.

Powodem spacji [jest to, że [jest to polecenie. Zwykle wbudowane w powłokę. Powłoka wykonuje polecenie [z resztą jako parametrami, w tym ]jako obowiązkowy ostatni parametr. Jeśli nie umieścisz spacji po [tym, jak powłoka spróbuje wykonać [whateverpolecenie i zakończy się niepowodzeniem.

Powód miejsca przed tym ]jest podobny. Ponieważ w przeciwnym razie nie zostanie rozpoznany jako parametr sam w sobie.

lesmana
źródło
To było na miejscu, ale teraz dostaję test.sh: line 6: [: missing ] ''
Jåcob
@lesmana. Dobry sposób, aby najpierw podać błędną odpowiedź, a następnie kontynuować edycję, zanim ktoś inny udzieli poprawnej odpowiedzi. Spróbuj podać poprawną odpowiedź za pierwszym razem.
Valentin Bajrami,
1
Nie uważam mojej pierwszej odpowiedzi za błędną. W rzeczywistości było to „na miejscu”. To po prostu nie rozwiązało wszystkich problemów w pytaniu.
lesmana
if jest składnią, to nie jest zwykłe polecenie. To zastrzeżone słowo. W przeciwieństwie do wielu innych języków programowania, powłoka nie wszędzie rozpoznaje słowa zastrzeżone, tylko gdy są one pierwszym słowem polecenia (z kilkoma subtelnościami).
Gilles „SO- przestań być zły”
Dziękuję za wyjaśnienie. Wiem, że ifto jest składnia. Próbowałem zakomunikować, że część warunku ifnie jest ograniczona do pewnej formy przez składnię. Zredagowałem tekst. Mam nadzieję, że teraz jest jaśniej.
lesmana
5

Możesz łatwo sprawdzić skrypty powłoki za pomocą ShellCheck online (dostępny również jako samodzielne narzędzie).

W takim przypadku będzie wskazywać, że instrukcja if wymaga spacji, [przed i przed ], oraz że potrzebujesz ;(lub nowego wiersza) przed thentym samym wierszem.

Kiedy to naprawisz, wyświetli się informacja, że USER_NAMEjest używany bez inicjowania czegokolwiek. Jest tak, ponieważ masz również user_namezmienną (wielkość liter ma znaczenie). To samo dotyczy PASSi pass.

Mówi ci również, abyś używał, read -raby powstrzymać się readod zniekształcania \(może to być ważne na przykład w przypadku haseł) oraz że powinieneś podwójnie cytować zmienne podczas wywoływania, sqlplusaby zapobiec przypadkowemu globalizacji nazw plików i dzieleniu słów przez powłokę (ponownie jest to ważne, jeśli hasło zawiera na przykład znaki globowania plików, takie jak *spacje).

Wcięcie kodu sprawi, że będzie on również bardziej czytelny:

#!/bin/bash

read -r -p 'please enter username: ' user_name
IFS= read -rs -p 'please enter password: ' pass

printf 'ORACLE_SID = %s\n' "$ORACLE_SID"
sid=$ORACLE_SID

if [ "$sid" = 'Test' ]; then
    echo 'Cannot copy' >&2
    exit 1
fi

sqlplus -s -l "$user_name/$pass@$sid" <<'SQL_END'
copy from scott/tiger@orcl insert EMP using select * from EMP
exit
SQL_END

Tutaj umożliwiłem także używanie haseł z wiodącymi lub końcowymi znakami spacji, tymczasowo ustawiając IFSpusty ciąg znaków do odczytu hasła read.

Zmieniono również logikę, aby ratować, jeśli $ORACLE_SID/ $sidis Test. Pozwala to uniknąć posiadania głównej części operacyjnej skryptu w ifgałęzi.

Kusalananda
źródło
Pamiętaj, że if ([ x = x ]) then (echo yes) firównież działa.
Stéphane Chazelas,
@ StéphaneChazelas Ach, tak. I może to być interesujące z punktu widzenia programu generującego kod powłoki, ale nie tak zwykle pisze się ifinstrukcje z [ ... ]... :-)
Kusalananda
2

Podczas pisania shchcesz

if [ "$ORACLE_SID" != "Test" ]
then
  ...
fi

Podczas pisania bash

if [[ "$ORACLE_SID" != "Test" ]]
then
  ...
fi

Uważaj na przestrzenie, proszę. Musi być odstęp między [[pierwszym operatorem.

Valentin Bajrami
źródło