Erreur de syntaxe Bash lorsque "else" suit une clause "then" vide

36

Pourquoi le script suivant ne serait-il pas exécuté, mais donnerait une erreur de syntaxe de else:

LOGS3_DIR=~/logs
if [ -d "$LOGS3_DIR" ]; then
 cd
 cd "$LOGS3_DIR"
 echo "$LOGS3_DIR"
 for filename in `find "." -mtime 1 -type f`
  do
  if lsof "$filename" > /dev/null
  then
    # file is open
  else
    echo "deleting $filename"
    rm "$filename"
  fi
 done
fi
Utilisateur novice
la source

Réponses:

23

N'utilisez pas de substitution de commande à la sortie defind . Ici, tout peut être fait avec find:

find . -mtime 1 -type f ! -exec lsof -t {} \; -exec rm -f {} \; > /dev/null

Avec quelques findimplémentations (y compris FreeBSD d' findoù il provient et GNU find), vous pouvez utiliser à la -deleteplace de -exec rm....

La raison pour laquelle vous obtenez une erreur est qu’il n’ya pas de commande entre thenetelse et que certains shells (à commencer par le shell Bourne d'où provient cette syntaxe) en nécessitent au moins un (et un commentaire n'est pas une commande). Notez que c'est complètement arbitraire et qu'il n'y a aucune raison pour que ces obus fassent cela. yashet zshne pas avoir cette limitation ( if false; then else echo x; fiet même if false; then else fibien travailler avec eux).

Comme d’autres l'ont déjà dit, vous pouvez utiliser une commande noop du type :(ou for nothing in; do nothing; done) ou inverser la logique avec le !mot clé (disponible dans les shells POSIX, mais pas dans le shell Bourne (vous constaterez que l'utilisation de: de cela était courante dans ce shell)). mkshet yasharriver à soutenir if false; then () else echo x; fi(je ne compterais pas dessus car cela pourrait changer dans les versions futures cependant).

Une autre approche est avec:

lsof... || {
  cmd1
  cmd2
}

si une différence est le statut de sortie global qui sera celui de lsofsi lsoféchoue.

Stéphane Chazelas
la source
17
Bien que ce soit une bien meilleure façon de faire ce que tente l'utilisateur de @Novice, cela ne répond pas du tout à la question.
SeeJayBee
Bien que -execsouvent utile, tel quel xargs, une boucle shell est parfois nécessaire. Dans ce cas, une while read nameboucle est l'option préférée (dans bash avec GNU find, vous pouvez utiliser l'option -0 pour les deux; vous devez obligatoirement renoncer à la nouvelle ligne).
Jan Hudec
@ JanHudec, il existe des moyens portables. -print0is -exec printf '%s\0' {} +(mais de manière portable, vous ne pouvez pas gérer cette sortie sauf si vous souhaitez en tenir compte perl), et avec find .//.et certains post-traitements, vous pouvez échapper aux nouvelles lignes xargs. Notez que ce n'est pas un while read, c'est while IFS= read -r.
Stéphane Chazelas
@Chris, j'ai ajouté une réponse à la question car cette réponse a finalement été acceptée.
Stéphane Chazelas
91

Il semble que vous vouliez faire un no-op si le fichier est ouvert, vous devriez donc ajouter un :, qui est une commande nulle dans bash:

if lsof "$filename" > /dev/null; then
  # file is open
  :
else
  printf 'deleting %s\n' "$filename"
  rm -- "$filename"
fi

Si vous n'utilisez pas :, bashne peut pas analyser votre code, et affichera une erreur comme bash: syntax error near unexpected token 'else'.

cuonglm
la source
jamais nouveau :et c'est la première commande listée dans bash-builtins.
bolov
26

Une autre alternative: inversez votre logique.

if ! lsof "$filename" >/dev/null;then
    echo "deleting $filename"
    rm "$filename"
fi
Joseph R.
la source
17

TL; DR

Aucune des autres réponses n'aborde réellement votre question initiale de savoir pourquoi la commande génère une erreur de syntaxe. Ceci est dû à une commande manquante entre then et else .

Une commande manquante

Votre code original ressemble à ceci:

if lsof "$filename" > /dev/null
then
  # file is open
else
  echo "deleting $filename"
  rm "$filename"
fi

Le problème est que vous avez un commentaire entre then et else , mais que le commentaire n'est pas traité comme une commande. En bref, vous pouvez réécrire le problème que vous avez (structurellement parlant) comme suit:

$ if true; then else echo; fi
bash: syntax error near unexpected token `else'

Corrigez votre syntaxe avec Bourne Builtin

Vous pouvez résoudre ce problème en plaçant des commandes réelles avant les autres , mais un commentaire par lui - même pas. La section if-then ne peut être vide; si vous voulez un espace réservé, vous pouvez utiliser le colon intégré . Par exemple:

$ if true; then :; else echo; fi

Placer simplement :dans la section entre alors et else résoudra l’erreur de syntaxe que vous rencontrez.

CodeGnome
la source
1
Gnouc answer, qui est aussi la plus votée, répond déjà à la question initiale.
jlliagre
Ne répondez que pour corriger l'erreur de syntaxe. FWIW, vous pouvez reproduire une erreur similaire avec un seul point-virgule au début d'une ligne. Cela donnera un indice fort. $ ; -bash: syntax error near unexpected token ';'
Matthew Hannigan