57

Why would following script not execute, but give a syntax error of 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

4 Answers 4

131

It seems that you want to do a no-op if the file is open so you should add a :, which is a null command in bash:

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

If you don't use :, bash can not parse you code, and will show error like bash: syntax error near unexpected token 'else'.

1
  • 6
    never new of : and it's the first command listed in bash-builtins.
    – bolov
    Nov 12, 2015 at 14:02
27

Another alternative: reverse your logic.

if ! lsof "$filename" >/dev/null;then
    echo "deleting $filename"
    rm "$filename"
fi
0
24

Don't use command substitution on the output of find. Here, everything can be done with find:

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

With a few find implementations (including FreeBSD find where it comes from and GNU find), you can use -delete instead of -exec rm....

The reason you're getting an error is that there's no command between then and else and some shells (starting with the Bourne shell where that syntax comes from) require at least one (and a comment is not a command). Note that it is completely arbitrary and there is no reason why those shells would do that. yash and zsh don't have that limitation (if false; then else echo x; fi and even if false; then else fi work fine with them).

As others have said, you can use a noop command like : (or for nothing in; do nothing; done) or reverse the logic with the ! keyword (available in POSIX shells, but not the Bourne shell (you'll find that using : for that was common in that shell)). mksh and yash happen to support if false; then () else echo x; fi (I wouldn't rely on it as that could change in future versions though).

Another approach is with:

lsof... || {
  cmd1
  cmd2
}

though one difference is the overall exit status which will be that of lsof if lsof fails.

4
  • 22
    While this is a much better way to do what @Novice User is attempting, it doesn't answer the question at all.
    – SeeJayBee
    Jun 2, 2014 at 18:49
  • While -exec is often useful, as is xargs, sometimes a shell loop is needed. In which case a while read name loop is the preferred option (in bash with GNU find you can use the -0 option for both; portably you have to give up on newline).
    – Jan Hudec
    Jun 3, 2014 at 8:06
  • @JanHudec, There are ways portably. -print0 is -exec printf '%s\0' {} + (but portably you can't deal with that output except if you want to consider perl), and with find .//. and some post-processing, you can escape the newlines for xargs. Note that it's not a while read, it's while IFS= read -r. Jun 3, 2014 at 8:29
  • @Chris, I've added an answer to the actual question since the answer ended-up being accepted. Mar 2, 2017 at 12:53
23

TL;DR

None of the other answers actually addresses your original question of why the command gives a syntax error. This is caused by a missing command between then and else.

A Missing Command

Your original code looks like this:

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

The problem is that you have a comment between then and else, but the comment isn't treated as a command. In short, you could rewrite the problem you have (structurally speaking) as follows:

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

Fix Your Syntax with a Bourne Builtin

You can fix this problem by placing actual commands before else, but a comment by itself won't do. The if-then section can't be empty; if you want a placeholder, you can use the colon builtin. For example:

$ if true; then :; else echo; fi

Simply placing : into the section between then and else will fix the syntax error you are experiencing.

3
  • 2
    Gnouc answer, which is also the most upvoted one, is already adressing the original question.
    – jlliagre
    Jun 2, 2014 at 1:19
  • Only answer to address the syntax error. FWIW, you can reproduce a similar error with a sole semi-colon at the beginning of a line. This will give a strong hint. $ ; -bash: syntax error near unexpected token ';' Jun 5, 2014 at 10:59
  • Upvoting for the link to the Bash manual! May 20, 2021 at 5:48

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .