Handling file paths with white space in bash

When coding in bash, files and directories with spaces (like GPS 1.3) can be particularly tricky. I bumped into this problem as I was trying to make the package for Gimp Paint Studio for Arch User Repository. Handling white space in your shell script is not so intuitive, and it differs a bit from how you do it on the command line, but I’ve finally got it.

First of all, here’s what happens to a string when you write it in your bash script.

Let’s prepare the working environment. In your home directory, create a directory called Directory with spaces. Inside that directory, touch a few files. I named my files file1, file2, and file3.

Create a shell script and save the following content in there:

#!/bin/bash

echo "First try"
ls ~/Directory with spaces

echo "Second try"
ls ~/Directory\ with\ spaces

echo "Third try"
ls "~/Directory with spaces"

echo "Fourth try"
ls "~/Directory\ with\ spaces"

After executing the script, you will see the following output:

First try
ls: cannot access /home/suzana/Directory: No such file or ...
ls: cannot access with: No such file or directory
ls: cannot access spaces: No such file or directory
Second try
file1  file2  file3
Third try
ls: cannot access ~/Directory with spaces: No such file or ...
Fourth try
ls: cannot access ~/Directory\ with\ spaces: No such file or ...

Clearly, only one solution works. And the winner is: second solution. In the second solution, we only used escape character to mark the spaces.

Ok, let’s modify our script a little.

!/bin/bash

VARIABLE="Directory with spaces"

echo "First try"
ls $VARIABLE

echo "Second try"
ls "$VARIABLE"

echo "Third try"
ls ${VARIABLE// /\\ /}

This time we’ve used a variable. (Don’t add spaces around the assignment operator =.) And the results are:

First try
ls: cannot access Directory: No such file or directory
ls: cannot access with: No such file or directory
ls: cannot access spaces: No such file or directory
Second try
file1  file2  file3
Third try
ls: cannot access Directory\: No such file or directory
ls: cannot access /with\: No such file or directory
ls: cannot access /spaces: No such file or directory

Again the second solution works, which is quoting the variable. How about something more exotic this time? How about our own version of ls?

!/bin/bash

for file in ~/Directory\ with\ spaces/*
do
    echo "Let's go"
    echo "$file"
    echo $file
    echo ${file// /\\ }
done

The results are:

Let's go
/home/suzana/Directory with spaces/file1
/home/suzana/Directory with spaces/file1
/home/suzana/Directory\ with\ spaces/file1
...

First thing first. The for loop (3rd line) uses the same method as the first example. I’ve tried other methods, but this one was the only one that works Also, I’ve included three echo lines to demonstrate how different ways of using the variable work. You have to note that the first and second echo commands yield similar results, but we already know that when passing file paths that contain spaces to commands, only the quoted variable works.

The last echo line is a very cool trick I’ve learned. When substituting variable using the { }, you can also do a quick string substitution. The syntax is:

${varname/string/replace} # replace first 'string' with 'replace'
${varname//string/replace} # replace all 'string' with 'replace'

I hope this will come in handy sometimes. Happy hacking!

Comments

Setting global configuration for Git

If you are using Git for revision control, you want to set some basic global configuration options before you start using it. Here are some of the common ones.

Set the user’s name:

$ git config --global user.name "Branko Vukelic"

Set user’s e-mail address:

$ git config --global user.email bg.branko@gmail.com

Colorize Git output:

$ git config --global color.ui auto

Set aliases for various Git commands:

$ git config --global alias.br branch
$ git config --global alias.st status
$ git config --global alias.co checkout
$ git config --global alias.ci commit

Comments

IE at 5am

me: that's how I used to work

but if I did that now

I would end up

feeling like the world is crumbling in front of my eyes

: D

Branko: for me it's easier than staring into IE's crap

me: getting everything just right and then opening IE is so disappointing

I don't think I could take it

: D

especially at 5am

Branko: :D

luckily, it's 4AM now

: )

Comments

Mcabber Sound Effects

So yo love Mcabber, that little ncurses Jabber client, the most handsome of them all? Sure you do.

Mcabber on Arch Linux / urxvt

Here’s a handy script to get you the sound going.

#!/usr/bin/python
# Version 0.05-sound
#
#  Copyright (C) 2007 Adam Wolk "Mulander" <netprobe@gmail.com>
#  Slightly updated by Mikael Berthe
#  Hacked apart by Branko "Foxbunny" Vukelic <bg.branko@gmail.com>
#
# To use this script, set the "events_command" option to the path of
# the script (see the mcabberrc.example file for an example)
#
# This script is provided under the terms of the GNU General Public License,
# see the file COPYING in the root mcabber source directory.
#
import sys, os

CMD_MSG_IN = "aplay -f cd %s/.mcabber/notify.wav" % os.path.expanduser("~")

if len(sys.argv) == 5:
    event,arg1,arg2,filename = sys.argv[1:5]
else:
    event,arg1,arg2          = sys.argv[1:4]
    filename                 = None

if event == 'MSG' and (arg1 == 'IN' or arg1 == 'MUC'):
    import subprocess
    command = CMD_MSG_IN.split()
    try:
        subprocess.call(command)
    except:
        pass

Copy the above into a notify.py file in ~/.mcabber directory, and install python and alsaplayer. Find a suitable wav file and copy it into ~/.mcabber/notify.wav.

Add the following line into your ~/.mcabber/mcabberrc:

set events_command = ~/.mcabber/notify.py

Just remember not to use any crackling or annoying sound (unless you really want to hurt your own ears).

Comments

Getting the Working Directory in Python

Sometimes you need to get the current working directory for your script. There is a nice little function that gets you just that, and it’s called getcwd. Here’s a small example:

Create a file called getcwd.py and put the following in there:

#!/bin/env python
import os
print os.getcwd()

Now you run it like this:

$ python getcwd.py 
/home/foxbunny
$ cd Video
$ python ~/getcwd.py 
/mnt/storage/Video 

You have to note that the working directory is the directory from which you run the script, not where the script is physically located.

Comments

Testing Django Template Tags

Writing custom template tags for Django is fun. But testing the tags is a conceptual challenge for me. As Ironfroggy says in one of his blog posts:

So how do you test something that is so entrenched in the Django processing pipeline as a template tag?

I’m used to test-driven development since my web.py days, but now that I’m back on Django, the complexity of the beast seems overwhelming. Ironfroggy’s posts didn’t help either. They are way over my current knowledge.

So, getting prepared for tomorrow’s sprint, I’m reviewing the template engine API and trying to figure out how to do the tests for Djassets, the new version of the old Django site assets app.

Surprisingly enough (not), I’ve managed to dig out some code on Github that does something similar to what I am trying to do. The tests in that code also provides a hint at how I should proceed myself.

I’m very excited about the tomorrow’s sprint. But first I need to do some serious reading.

Comments

Testing Standalone Django Apps

Comments