Erreur de syntaxe près du jeton inattendu `fi`

17

Je ne veux pas nécessairement la réponse, mais si quelqu'un pouvait me pointer vers de la littérature ou des exemples. Je voudrais le comprendre.

Lorsque j'exécute le script, je reçois une erreur:

Erreur de syntaxe près d'un jeton inattendu fi

J'ai déduit que mon problème est dans ma ifdéclaration en faisant mes ifcommentaires et en ajoutant echo "$NAME"qui affiche les noms dans le /etc/.

Quand je fais des changements, retirez le #de ifet fiet ajouter #à wc -c "$NAME", je reçois l'erreur de syntaxe I ci - dessus. J'ai ajouté ;entre- ]temps. Je suis également passé thenà la ligne suivante sans résolution.

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

     if [ -r "$NAME" -af "$NAME" ] then
          wc -c "$NAME"
     fi
done
Christina A Guzman
la source
9
Chaque fois que vous avez une erreur de syntaxe du shell, une bonne première étape consiste à couper et coller votre code dans shellcheck.net et à corriger les erreurs qu'il identifie. Si vous avez du mal à comprendre ses messages, venez ici et demandez.
John1024
2
Qu'est-ce que c'est -afcensé faire?
ilkkachu
Merci pour ce site @ John1024, il a été très utile, je l'ai mis en signet pour référence future.
Christina A Guzman
1
@ChristinaAGuzman Dans un tel cas -aest redondant car la condition est déjà incluse dans -f. --- Quoi qu'il en soit, plusieurs conditions [ ](cette commande est également disponible en tant que test) doivent être jointes à l'aide d'opérateurs logiques comme -a(et) ou -o(ou), mais comme cela a été suggéré dans la réponse ci-dessous, il est préférable d'utiliser plusieurs commandes [ ]( test) et de joindre les utilisant des opérateurs shell comme &&ou ||.
pabouk
2
Christina, comme l'a souligné @ John1024, shellcheck.net montre très clairement une syntaxe ou des erreurs encore plus profondes (comme le fait le lint pour le code C). Je recommande également une lecture incontournable: mywiki.wooledge.org/BashPitfalls (lisez-le au moins une fois!) Et mywiki.wooledge.org/BashFAQ et mywiki.wooledge.org/BashGuide sont également à lire.
Olivier Dulac

Réponses:

46

Mots - clés comme if, then, else, fi, for, caseet ainsi de suite doivent être dans un endroit où le shell attend un nom de commande. Sinon, ils sont traités comme des mots ordinaires. Par exemple,

echo if

imprime simplement if, il ne démarre pas une instruction conditionnelle.

Ainsi, dans la ligne

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

le mot thenest un argument de la commande [(dont il se plaindrait s'il venait à s'exécuter). Le shell continue de chercher le thenet trouve une fiposition de commande. Puisqu'il y en ifa toujours un qui le recherche then, fic'est inattendu, il y a une erreur de syntaxe.

Vous devez mettre un terminateur de commande avant thenafin qu'il soit reconnu comme mot-clé. Le terminateur de commande le plus courant est un saut de ligne, mais avant then, il est courant d'utiliser un point-virgule (qui a exactement la même signification qu'un saut de ligne).

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

ou

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

Une fois que vous aurez corrigé cela, vous obtiendrez une autre erreur de la commande [car elle ne comprend pas -af. Vous vouliez probablement dire

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

Bien que les commandes de test ressemblent à des options, vous ne pouvez pas les regrouper comme ceci. Ce sont des opérateurs de la [commande et ils doivent chacun être un mot distinct (comme le font [et ]).

Soit dit en passant, bien que cela [ -r "$NAME" -a -f "$NAME" ]fonctionne, je recommande d'écrire soit

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

ou

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

Il est préférable de garder les [ … ]conditions simples car la [commande ne peut pas distinguer facilement les opérateurs de l'opérande. Si $NAMEressemble à un opérateur et apparaît dans une position où l'opérateur est valide, il peut être analysé en tant qu'opérateur. Cela ne se produira pas dans les cas simples présentés dans cette réponse, mais les cas plus complexes peuvent être risqués. L'écriture avec des appels séparés [et l'utilisation des opérateurs logiques du shell évite ce problème.

La seconde syntaxe utilise la [[ … ]]construction conditionnelle qui existe dans bash (et ksh et zsh, mais pas plain sh). Cette construction est une syntaxe spéciale, alors [est analysée comme une autre commande, donc vous pouvez utiliser des choses comme à l' &&intérieur et vous n'avez pas besoin de variables de citation , sauf dans les arguments à certains opérateurs de chaîne ( =, ==, !=, =~) (voir Quand des guillemets doubles nécessaire ? pour plus de détails).

Gilles 'SO- arrête d'être méchant'
la source
Notez que cela if [[ 1 ]] then [[ 2 ]] fifonctionne dans ksh, pdksh et zsh. if(:)then(:)fifonctionne dans toutes les coquilles.
Stéphane Chazelas
12

Voir ce qui a changé comme suit

if [-r "$ NAME" -a -f "$ NAME"] ; ensuite
# ^^^^^ ^
     wc -c "$ NAME"
Fi

Si vous souhaitez supprimer toutes les commandes du bloc if, vous devez au moins y ajouter deux points, comme

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

ou version une ligne

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

Bruce
la source
2

D'autres l'ont déjà souligné, mais si vous cherchez une référence officielle, alors RTM

si liste; puis listez; [liste elif; puis listez; ] ... [else list;] fi

La liste if est exécutée. Si son état de sortie est zéro, la liste alors est exécutée. Sinon, chaque liste elif est exécutée à son tour, et si son état de sortie est nul, la liste correspondante est exécutée et la commande se termine. Sinon, la liste else est exécutée, le cas échéant. L'état de sortie est l'état de sortie de la dernière commande exécutée, ou zéro si aucune condition n'est vérifiée.

Vous manquez le ;

Et la syntaxe de listest décrite dansman test

Kashyap
la source