Błąd składni w pobliżu nieoczekiwanego tokena `fi`

17

Niekoniecznie chcę odpowiedzi, ale jeśli ktoś mógłby wskazać mi literaturę lub przykłady. Chciałbym to rozgryźć.

Po uruchomieniu skryptu pojawia się błąd:

Błąd składni w pobliżu nieoczekiwanego tokena fi

Wydedukowałem, że mój problem tkwi w moim oświadczeniu, komentując ifmoje ifoświadczenia i dodając, echo "$NAME"który wyświetla nazwy w /etc/.

Kiedy wprowadzam zmiany, usuwam #z ifi fidodawam #do wc -c "$NAME", pojawia się błąd składni wymieniony powyżej. Dodałem ;między ]tym. Przeszedłem również thendo następnego wiersza bez rozdzielczości.

#!/bin/bash
for NAME in /etc/*
do

     if [ -r "$NAME" -af "$NAME" ] then
          wc -c "$NAME"
     fi
done
Christina A Guzman
źródło
9
Ilekroć pojawia się błąd składniowy powłoki, dobrym pierwszym krokiem jest wycięcie i wklejenie kodu do shellcheck.net i poprawienie zidentyfikowanych błędów. Jeśli masz problem ze zrozumieniem przesłania, przyjdź tutaj i zapytaj.
John1024,
2
Co to -afma zrobić?
ilkkachu
Dziękuję za tę stronę @ John1024, która była najbardziej przydatna, dodałem ją do zakładek do wykorzystania w przyszłości.
Christina A Guzman
1
@ChristinaAGuzman W takim przypadku -ajest zbędny, ponieważ warunek jest już uwzględniony -f. --- W każdym razie wiele warunków w obrębie [ ](to polecenie jest również dostępne jako test) należy połączyć za pomocą operatorów logicznych, takich jak -a(i) lub -o(lub), ale jak sugerowano w odpowiedzi poniżej, lepiej jest używać wielu [ ]( test) poleceń i łączyć za pomocą operatorów powłoki takich jak &&lub ||.
pabouk
2
Christina, jak wskazano @ John1024, shellcheck.net bardzo wyraźnie pokazuje niektóre składnie lub nawet głębsze błędy (jak robi to lint w kodzie C). Polecam także lekturę: mywiki.wooledge.org/BashPitfalls (przeczytaj to przynajmniej raz!) Oraz mywiki.wooledge.org/BashFAQ i mywiki.wooledge.org/BashGuide również dobrze czytać.
Olivier Dulac,

Odpowiedzi:

46

Słowa kluczowe jak if, then, else, fi, for, casei tak dalej musi być w miejscu, gdzie powłoka oczekuje nazwę polecenia. W przeciwnym razie są traktowane jak zwykłe słowa. Na przykład,

echo if

po prostu drukuje if, nie uruchamia instrukcji warunkowej.

Tak więc w kolejce

if [ -r "$NAME" -af "$NAME" ] then

słowo thenjest argumentem polecenia [(na które narzekałby, gdyby kiedykolwiek mógł uruchomić). Powłoka wciąż szuka theni znajduje fipozycję dowodzenia. Ponieważ jest coś if, co wciąż go szuka then, fijest to nieoczekiwane, wystąpił błąd składniowy.

Musisz wcześniej wstawić terminator poleceń then, aby został rozpoznany jako słowo kluczowe. Najczęstszym terminatorem poleceń jest podział linii, ale wcześniej thenczęsto stosuje się średnik (który ma dokładnie takie samo znaczenie jak podział linii).

if [ -r "$NAME" -af "$NAME" ]; then

lub

if [ -r "$NAME" -af "$NAME" ]
then

Po naprawieniu [pojawi się kolejny błąd polecenia, ponieważ nie rozumie -af. Prawdopodobnie miałeś na myśli

if [ -r "$NAME" -a -f "$NAME" ]; then

Chociaż polecenia testowe wyglądają jak opcje, nie można ich w taki sposób spakować. Są operatorami [polecenia i muszą być osobnymi słowami (tak jak [i ]).

Nawiasem mówiąc, chociaż [ -r "$NAME" -a -f "$NAME" ]działa, polecam albo pisać

[ -r "$NAME" ] && [ -f "$NAME" ]

lub

[[ -r $NAME && -f $NAME ]]

Najlepiej jest zachować [ … ]warunki warunkowe, ponieważ [polecenie nie może łatwo odróżnić operatorów od operandu. Jeśli $NAMEwygląda jak operator i pojawia się w pozycji, w której operator jest poprawny, można go przeanalizować jako operator. Nie zdarzy się to w prostych przypadkach przedstawionych w tej odpowiedzi, ale bardziej złożone przypadki mogą być ryzykowne. Pisanie tego z osobnymi wywołaniami [i używanie operatorów logicznych powłoki pozwala uniknąć tego problemu.

Druga składnia wykorzystuje [[ … ]]konstrukcję warunkową, która istnieje w bash (i ksh i zsh, ale nie zwykły sh). Ta konstrukcja jest specjalna składnia, natomiast [jest analizowany jak inne polecenia, więc można używać rzeczy jak &&wewnątrz i nie trzeba do zmiennych cytując wyjątkiem argumenty niektórych operatorów strunowych ( =, ==, !=, =~) (patrz Kiedy jest to konieczne dwukrotne cytowanie ? po szczegóły).

Gilles „SO- przestań być zły”
źródło
Zauważ, że if [[ 1 ]] then [[ 2 ]] fidziała w ksh, pdksh i zsh. if(:)then(:)fidziała we wszystkich powłokach.
Stéphane Chazelas,
12

Zobacz, co się zmieniło w następujący sposób

if [-r „$ NAME” -a -f „$ NAME”] ; następnie
# ^^^^^ ^
     wc -c „$ NAME”
fi

Jeśli chcesz usunąć wszystkie polecenia w bloku if, musisz przynajmniej dodać dwukropek

if [ -r "$NAME" -a -f "$NAME" ]; then
    :
fi

lub wersja jednoliniowa

if [ -r "$NAME" -a -f "$NAME" ]; then :; fi

Bruce
źródło
2

Inni już zauważyli, ale jeśli szukasz oficjalnej referencji, to RTM

lista; następnie lista; [lista elif; następnie lista; ] ... [else list;] fi

Lista if jest wykonywana. Jeśli jego kodem wyjścia jest zero, wówczas wykonywana jest lista. W przeciwnym razie każda lista elifów jest wykonywana po kolei, a jeśli jej status wyjścia wynosi zero, wykonywana jest odpowiednia lista następnie i komenda jest wykonywana. W przeciwnym razie lista else zostanie wykonana, jeśli jest dostępna. Status wyjścia to status wyjścia ostatniego wykonanego polecenia lub zero, jeśli żaden warunek nie jest spełniony.

Tęsknisz za ;

Składnia listjest opisana wman test

Kashyap
źródło