Working with filenames containing spaces in Bash
The problem: spaces in filenames
Sometimes you’ll need to manipulate a list of filenames which contain spaces using a ‘for’ loop. Let’s say you have the following two files:
Command:
$ ls -1
Output:
Aisling, Sanda and Justin.jpg
Monika, Sanda, Justin, Michelle and Ben.jpg
They list perfectly well - one file on each line. (I used the ‘-1’ [minus one] option to ‘ls’ to make a single-column list)
However something strange happens when you want to reference them individually for a ‘for’ loop. Here I use a ‘for’ loop to see how the filenames are being passed to the ‘echo’ command:
Command:
$ for line in $(ls -1) ; do echo ${line} ; done
Aisling,
Sanda
and
Justin.jpg
Monika,
Sanda,
Justin,
Michelle
and
Ben.jpg
Aaagh! The filenames are being chopped up at the spaces and at the newlines!
The solution: a custom value for $IFS
I need to be able to tell bash that I only want it to chop up lists at the newline character. For this, I modify the value of the built-in variable, IFS. The name stands for ‘Internal Field Separator’.
IFS normally has the value ” ” - that’s to say, space, tab and newline. Let’s reset it so that it only contains the newline character:
$ IFS=$‘’
See how I specify the newline character? That’s important. It’s different from how you’d normally set a shell variable. I don’t want the literal string ‘’, I want what that string represents - a newline.
Now I run the same ‘for’ loop again:
Command:
$ for line in $(ls -1) ; do echo ${line} ; done
Aisling, Sanda and Justin.jpg
Monika, Sanda, Justin, Michelle and Ben.jpg
Now I can manipulate the filenames as I originally intended.
An example: get rid of those spaces!
Here’s a shell script that uses the ‘tr’ command to rename a list of files, replacing tab and space characters in filenames with underscores:
#!/bin/bash
# Back up the current value of IFS
IFS=$IFS
IFS=$‘’
for line in $(ls -1) ; do
newname=$(echo ${line} | tr ’ ’ ’’)
escapedname=$(echo ${line} | tr ’ ’ “ ”)
mv ${escapedname} ${newname}
done
# Restore IFS
IFS=$_IFS
Here’s what my files look like after I’ve run that script:Aisling,_Sanda_and_Justin.jpg
Monika,_Sanda,_Justin,_Michelle_and_Ben.jpg