Bash Scripting – Handle Paths with Spaces and Wildcards

bashquotingshellwildcards

I am having trouble getting the basics of Bash scripting down. Here's what I have so far:

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

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

All I want to do is list all the .txt files in a for loop so I can do stuff with them. But the space in the my directory and the asterisk in *.txt just aren't playing nicely. I tried using it with and without double quotes, with and without curly braces on variable names and still can't print all the .txt files.

This is a very basic thing, but I'm still struggling because I'm tired and can't think straight.

What am I doing wrong?

I've been able to successfully apply the script above if my FILES don't have a space or an asterisk… I had to experiment with or without the use of double quotes and braces to get it to work. But the moment I have both spaces and an asterisk, it messes everything up.

Best Answer

Inside quotes, the * will not expand to a list of files. To use such a wildcard successfully, it must be outside of quotes.

Even if the wildcard did expand, the expression "${FILES}" would result in a single string, not a list of files.

One approach that would work would be:

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

In the above, file names with spaces or other difficult characters will be handled correctly.

A more advanced approach could use bash arrays:

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

In this case, FILES is an array of file names. The parens surrounding the definition make it an array. Note that the * is outside of quotes. The construct "${FILES[@]}" is a special case: it will expand to a list of strings where each string is one of the file names. File names with spaces or other difficult characters will be handled correctly.

Related Question