Robert Howerton
Robert Howerton

Reputation: 41

Listing directories with spaces using Bash in linux

I would like to create a bash script to list all the directories in a directory provided by the user via input, or all the directories in the current directory (given no input).

Here's what I have thus far, but when I execute it I encounter two problems.

1) The script completely ignores my input. The file is located on my desktop but when I type in "home" as the input, the script simply prints the directories of the Desktop (current directory).

2) The directories are printed on their own lines (intended) but it treats each word in a folder name as its own folder. i.e. is printed as:

this
folder

Here's the code I have so far:

#!/bin/bash

echo -n "Enter a directory to load files: "
read d

if [ $d="" ]; #if input is blank, assume d = current directory
then d=${PWD##*/} 
for i in $(ls -d */);
do echo ${i%%/};
done
else #otherwise, print sub-directories of given directory
for i in $(ls -d */);
do echo ${i%%/};
done
fi

Also in your response please explain your answer as I'm very new to bash.

Thanks for looking, I appreciate your time.

EDIT: Thanks to John1024's answer, I came up with the following:

#!/bin/bash

echo -n "Enter a directory to load files: "
IFS= read d
ls -1 -d "${d:-.}"/*/

And it does everything I need. Much appreciated!

Upvotes: 4

Views: 5829

Answers (1)

John1024
John1024

Reputation: 113814

I believe that this script accomplishes what you want:

#!/bin/sh
ls -1 -d "${1:-.}"/*/

Usage example:

$ bash ./script.sh  /usr/X11R6
/usr/X11R6/bin
/usr/X11R6/man

Explanation:

  • -1 tells ls to print each file/directory on a separate line

  • -d tells ls to list directories by name instead of their contents

  • The shell will ${1:-.} to be the first argument to the script if there is one or . (which means the current directory) if there isn't.

Enhancement

The above script displays a / at the end of each directory name. If you don't want that, we can use sed to remove trailing slashes from the output:

#!/bin/sh
ls -1d ${1:-.}/*/ | sed 's|/$||'

Revised Version of Your Script

Starting with your script, some simplifications can be made:

#!/bin/bash
echo -n "Enter a directory to load files: "
IFS= read d
d=${d:-$PWD}
for i in "$d"/*/
do
    echo ${i%%/}
done

Notes:

  • IFS= read d

    Normally leading and trailing white space are stripped before the input is assigned to d. By setting IFS to an empty value, however, leading and trailing white space will be preserved. Thus this will work even if the pathologically strange case where the user specifies a directory whose name begins or ends with white space.

    If the user enters a backslash, the shell will try to process it as an escape. If you don't like that, use IFS= read -r d and backslashes will be treated as normal characters, not escapes.

  • d=${d:-$PWD}

    If the user supplied a value for d, this leaves it unchanged. If he didn't, this assigns it to $PWD.

  • for i in "$d"/*/

    This will loop over every subdirectory of $d and will correctly handle subdirectory names with spaces, tabs, or any other odd character.

    By contrast, consider:

    for i in $(ls -d */)
    

    After ls executes here, the shell will split up the output into individual words. This is called "word splitting" and is why this form of the for loop should be avoided.

    Notice the double-quotes in for i in "$d"/*/. They are there to prevent word splitting on $d.

Upvotes: 3

Related Questions