Bash sintaks kesalahan ketika "lain" mengikuti klausa "lalu" kosong

36

Mengapa skrip berikut tidak dieksekusi, tetapi memberikan kesalahan sintaksis dari 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
Pengguna Baru
sumber

Jawaban:

23

Jangan gunakan substitusi perintah pada output darifind . Di sini, semuanya bisa dilakukan dengan find:

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

Dengan beberapa findimplementasi (termasuk FreeBSD dari findmana asalnya dan GNU find), Anda dapat menggunakannya -deletesebagai gantinya -exec rm....

Alasan Anda mendapatkan kesalahan adalah karena tidak ada perintah di antara thendan elsedan beberapa shell (dimulai dengan shell Bourne dari mana sintaks itu berasal) memerlukan setidaknya satu (dan komentar bukan perintah). Perhatikan bahwa itu sepenuhnya arbitrer dan tidak ada alasan mengapa cangkang itu melakukan itu. yashdan zshtidak memiliki batasan itu ( if false; then else echo x; fidan bahkan if false; then else fibekerja dengan baik dengan mereka).

Seperti yang orang lain katakan, Anda dapat menggunakan perintah noop seperti :(atau for nothing in; do nothing; done) atau membalikkan logika dengan !kata kunci (tersedia dalam shell POSIX, tetapi tidak pada shell Bourne (Anda akan menemukan bahwa menggunakan :untuk yang umum di shell itu)). mkshdan yashkebetulan mendukung if false; then () else echo x; fi(saya tidak akan mengandalkan itu karena itu bisa berubah di versi masa depan).

Pendekatan lain adalah dengan:

lsof... || {
  cmd1
  cmd2
}

meskipun satu perbedaan adalah status keluar keseluruhan yang akan menjadi lsofjika lsofgagal.

Stéphane Chazelas
sumber
17
Meskipun ini adalah cara yang jauh lebih baik untuk melakukan apa yang dicoba @Novice User, itu tidak menjawab pertanyaan sama sekali.
SeeJayBee
Meskipun -execsering berguna, seperti xargs, kadang-kadang dibutuhkan loop shell. Dalam hal ini while read nameloop adalah opsi yang disukai (dalam bash dengan GNU menemukan Anda dapat menggunakan opsi -0 untuk keduanya; Anda harus menyerah pada baris baru).
Jan Hudec
@ JanHudec, Ada beberapa cara yang mudah dibawa. -print0adalah -exec printf '%s\0' {} +(tapi Anda tidak bisa berurusan dengan output itu kecuali jika Anda ingin mempertimbangkan perl), dan dengan find .//.dan beberapa post-processing, Anda dapat keluar dari baris baru xargs. Perhatikan bahwa ini bukan while read, itu while IFS= read -r.
Stéphane Chazelas
@ Chris, saya telah menambahkan jawaban untuk pertanyaan yang sebenarnya sejak jawaban itu akhirnya diterima.
Stéphane Chazelas
91

Tampaknya Anda ingin melakukan no-op jika file terbuka sehingga Anda harus menambahkan :, yang merupakan perintah nol di bash:

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

Jika Anda tidak menggunakan :, bashtidak dapat menguraikan kode Anda, dan akan menampilkan kesalahan seperti bash: syntax error near unexpected token 'else'.

cuonglm
sumber
tidak pernah baru :dan ini adalah perintah pertama yang terdaftar di bash-builtin.
bolov
26

Alternatif lain: balikkan logika Anda.

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

TL; DR

Tidak ada jawaban lain yang benar-benar menjawab pertanyaan awal Anda mengapa perintah tersebut memberikan kesalahan sintaksis. Ini disebabkan oleh perintah yang hilang antara itu dan yang lain .

Perintah yang Hilang

Kode asli Anda terlihat seperti ini:

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

Masalahnya adalah Anda memiliki komentar antara saat itu dan yang lain , tetapi komentar tersebut tidak diperlakukan sebagai perintah. Singkatnya, Anda dapat menulis ulang masalah yang Anda miliki (secara struktural) sebagai berikut:

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

Perbaiki Sintaks Anda dengan Bourne Builtin

Anda dapat memperbaiki masalah ini dengan menempatkan perintah yang sebenarnya sebelum yang lain , tetapi komentar dengan sendirinya tidak akan melakukannya. Bagian jika-maka tidak boleh kosong; Jika Anda menginginkan placeholder, Anda dapat menggunakan builtin titik dua . Sebagai contoh:

$ if true; then :; else echo; fi

Cukup menempatkan :ke bagian antara itu dan yang lain akan memperbaiki kesalahan sintaks yang Anda alami.

CodeGnome
sumber
1
Jawaban Gnouc, yang juga merupakan yang paling banyak dipilih, sudah menjawab pertanyaan aslinya.
jlliagre
Hanya jawaban untuk mengatasi kesalahan sintaksis. FWIW, Anda dapat mereproduksi kesalahan yang sama dengan satu titik koma di awal baris. Ini akan memberi petunjuk yang kuat. $ ; -bash: syntax error near unexpected token ';'
Matthew Hannigan