Kesalahan skrip Bash dengan string dengan jalur yang memiliki spasi dan wildcard

25

Saya kesulitan mendapatkan dasar-dasar penulisan Bash. Inilah yang saya miliki sejauh ini:

#!/bin/bash
FILES="/home/john/my directory/*.txt"

for f in "${FILES}"
do
  echo "${f}"
done

Yang ingin saya lakukan adalah daftar semua .txtfile dalam satu forlingkaran sehingga saya dapat melakukan hal-hal dengan mereka. Tetapi ruang di my directorydan tanda bintang *.txthanya tidak bermain dengan baik. Saya mencoba menggunakannya dengan dan tanpa tanda kutip ganda, dengan dan tanpa kurung kurawal pada nama variabel dan masih tidak dapat mencetak semua .txtfile.

Ini adalah hal yang sangat mendasar, tetapi saya masih berjuang karena saya lelah dan tidak bisa berpikir jernih.

Apa yang saya lakukan salah?

Saya telah berhasil menerapkan skrip di atas jika FILES saya tidak memiliki ruang atau tanda bintang ... Saya harus bereksperimen dengan atau tanpa menggunakan tanda kutip ganda dan kawat gigi untuk membuatnya berfungsi. Tetapi saat saya memiliki spasi dan tanda bintang, itu mengacaukan segalanya.

John
sumber

Jawaban:

33

Di dalam kutipan, *tidak akan diperluas ke daftar file. Agar berhasil menggunakan wildcard seperti itu, itu harus di luar tanda kutip.

Bahkan jika wildcard berkembang, ekspresi "${FILES}"akan menghasilkan string tunggal, bukan daftar file.

Satu pendekatan yang akan berhasil adalah:

#!/bin/bash
DIR="/home/john/my directory/"
for f in "$DIR"/*.txt
do
  echo "${f}"
done

Di atas, nama file dengan spasi atau karakter sulit lainnya akan ditangani dengan benar.

Pendekatan yang lebih maju dapat menggunakan array bash:

#!/bin/bash
FILES=("/home/john/my directory/"*.txt)
for f in "${FILES[@]}"
do
  echo "${f}"
done

Dalam hal ini, FILESadalah array nama file. Parens yang mengelilingi definisi membuatnya menjadi array. Perhatikan bahwa di *luar tanda kutip. Konstruk "${FILES[@]}"adalah kasus khusus: itu akan diperluas ke daftar string di mana setiap string adalah salah satu nama file. Nama file dengan spasi atau karakter sulit lainnya akan ditangani dengan benar.

John1024
sumber
1
hebat yang berhasil
Yohanes
Perlu dicatat bahwa jika Anda melewati jalur seperti ini di sekitar fungsi, Anda perlu memastikan Anda mengutip variabelnya sendiri daripada menggabungkannya sebagai bagian dari string yang lebih besar: for f in "$DIR"/*.txt= baik for f in "$DIR/*.txt"= istirahat
pospi
7

Saat menggunakan array seperti yang ditunjukkan oleh John1024 jauh lebih masuk akal, di sini, Anda juga dapat menggunakan operator split + glob (membiarkan variabel skalar tidak dikutip).

Karena Anda hanya ingin bagian glob dari operator itu, Anda perlu menonaktifkan bagian yang terpecah :

#! /bin/sh -
# that also works in any sh, so you don't even need to have or use bash

file_pattern="/home/john/my directory/*.txt"
# all uppercase variables should be reserved for environment variables

IFS='' # disable splitting

for f in $file_pattern # here we're not quoting the variable so
                       # we're invoking the split+glob operator.
do
  printf '%s\n' "$f" # avoid the non-reliable, non-portable "echo"
done
Stéphane Chazelas
sumber
0

Yang dapat Anda lakukan adalah meninggalkan hanya karakter wildcard di luar tanda kutip.
Sesuatu seperti:
untuk di dalam "file dengan spasi" * ". Txt"
melakukan
pemrosesan
dilakukan
Jika wildcard itu sendiri meluas ke spasi, maka Anda perlu t menggunakan pendekatan file per baris, seperti menggunakan ls-l untuk menghasilkan daftar file dan gunakan bash read untuk mendapatkan setiap file.

Marcelo Pacheco
sumber
-1

Ketika Anda ingin memproses set file, Anda mempertimbangkan, di namanya mungkin ada ruang atau kode scape lain akan ada di sana, Jadi sebelum memulai proses Anda seperti for loopatau find commandatur IFS bash env variableke:

IFS=$(echo -en "\n\b")
Teluk Persia
sumber