「else」が空の「then」句の後に続く場合のbash構文エラー


36

次のスクリプトが実行されないのに、構文エラーが発生するのはなぜですか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

回答:


23

の出力でコマンド置換を使用しないでくださいfind。ここでは、すべてを次のように実行できますfind

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

いくつかのfind実装(FreeBSD findとGNUを含むfind)を使用すると、の-delete代わりに使用できます-exec rm...

エラーが発生する理由はthenelseandの間にコマンドがなく、一部のシェル(その構文が由来するBourneシェルから始まる)には少なくとも1つが必要なためです(コメントはコマンドではありません)。これは完全に任意であり、これらのシェルがそれを行う理由はないことに注意してください。その制限はyashありzshません(if false; then else echo x; fiそしてif false; then else fi、それらでうまく動作します)。

他の人が言ったように、:(またはfor nothing in; do nothing; done)のようなnoopコマンドを使用するか、!キーワードでロジックを逆にすることができます(POSIXシェルでは使用できますが、Bourneシェル:では使用できません(その使用はそのシェルで一般的でした))。mkshそして、yashサポートに起こるif false; then () else echo x; fi(つまりものの、将来のバージョンで変更される可能性として、私はそれに依存しないでしょう)。

別のアプローチは次のとおりです。

lsof... || {
  cmd1
  cmd2
}

ただし、1つの違いは、失敗したlsof場合の全体的な終了ステータスlsofです。


17
これは、@ Noviceユーザーが試みていることを行うためのはるかに優れた方法ですが、質問にはまったく答えません。
SeeJayBee 14年

一方で-exec、多くの場合に有用である、などであるxargs時々シェルループが必要とされています、。その場合、while read nameループが優先オプションです(GNU findを使用するbashでは、両方に-0オプションを使用できます。移植性を考慮して、改行を放棄する必要があります)。
ジャン・ヒューデック

@JanHudec、移植性のある方法があります。-print0-exec printf '%s\0' {} +(ただし、考慮したい場合を除いて、その出力を処理することはできませんperl)、find .//.およびいくつかの後処理を使用すると、の改行をエスケープできますxargs。でなく、while readであることに注意してくださいwhile IFS= read -r
ステファンシャゼル14年

@Chris、回答が受け入れられてから、実際の質問に回答を追加しました。
ステファンシャゼラス

91

ファイルが開いている場合はもしないようにしたいので、を追加する必要:があります。これはnullコマンドですbash

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

を使用しない場合:bashコードを解析できず、などのエラーが表示されますbash: syntax error near unexpected token 'else'


決して新しいものではなく:、bash-builtinsにリストされている最初のコマンドです。
-bolov

26

別の選択肢:ロジックを逆にします。

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

17

TL; DR

他の回答はどれも、コマンドが構文エラーを与える理由についての元の質問に実際に対処していません。これは、thenelseの間にコマンドがないために発生します

行方不明のコマンド

元のコードは次のようになります。

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

問題は、thenelseの間にコメントがあるが、コメントがコマンドとして扱われないことです。要するに、次のように(構造的に言えば)抱えている問題を書き換えることができます。

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

Bourneビルトインを使用して構文を修正する

elseの前に実際のコマンドを配置することでこの問題を修正できますが、コメントだけでは実行できません。IF-THENセクションでは、空にすることはできません。プレースホルダーが必要な場合は、コロンbuiltinを使用できます。例えば:

$ if true; then :; else echo; fi

単純に置く:との間の部分に、その後他のあなたが経験している構文エラーを修正します。


1
Gnoucの回答は、これも最も支持されているものですが、既に元の質問を解決しています。
jlliagre 14年

構文エラーに対処するためだけに答えてください。FWIWでは、行の先頭にセミコロンを1つ付けるだけで、同様のエラーを再現できます。これは強力なヒントになります。 $ ; -bash: syntax error near unexpected token ';'
マシューハニガン14年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.