Bash script to display directory listing gives error

I wrote a simple bash script (I’m pretty new to it, you know!) to accept a directory as its argument and print its listing, testing for files and directories. Here’s how I approached it:

#!/bin/bash
# Lists all files and directories (non-recursively) in a specified directory

if [ $# -gt 1 ]; then
    echo "Error. Please specify only one directory. ($#)"
    exit
fi

if [ -z $1 ]; then
    echo "No directory specified. Exiting."
    exit
fi

echo "Listing for $1:"

$dirs=`ls $1`
echo "Dirs: $dirs" # Just to confirm if all is well

# Loop through and print
for i in $dirs;
do
    if [ -f $i ]; then
        echo "File: $i"
    elif [ -d $i ]; then
        echo "Directory: $i"
    fi
done

The problem is in my for loop. When I run this script and feed it my home directory, I get this error:

./list_files_and_dirs.sh: line 16: =Calibre: command not found

I know I’m making a mistake in command substitution involving variables, but I just don’t know what. Someone please help!

================= Update =================

Here’s the new (final section) code as per inputs from answers:

dirs=`ls "$1"`
#echo "Dirs: $dirs" # Just to confirm if all is well

IFS=$'n'

# Loop through and print
for i in $dirs;
do
    if [ -f "$i" ]; then
        echo "File: $i"
    elif [ -d "$i" ]; then
        echo "Directory: $i"
    fi
done

Answer

Note: I’ve assumed you’re teaching yourself Bash. Do not use this code in production. find "$directory" -maxdepth 1 -type d will get you the directories, -type f will get you the files.

Since it is complaining about line 16, let’s have a look at it:

$dirs=`ls $1`

If you want to assign to a variable, you should not include the $. I’m guessing you meant:

dirs=`ls $1`

What’s happening now, is this:

  • $dirs is probably empty, so substituted with nothing.
  • The ls command is run, and its output substituted in the ‘command’.
  • The first file in your directory is called Calibre, leaving you with the command: =Calibre x y z ...
  • =Calibre is not a valid command, thus the error.

However, we’re not there yet: this will go wrong if there are spaces in any of the filenames. To fix that, you need to do more:

  • You need to include IFS=$'n' somewhere before the for loop. This sets the fields separator to the linefeed character, which stops the for loop from splitting up the files on the spaces or tabs (it’s technically possible for a file to have a linefeed in its filename as well: you’re unlikely to ever encounter such a file and it won’t cause serious problems in this case if you do run into such a file, but it’s worth being aware of the possibility in case it ever does matter).
  • In order for a filename with spaces not to turn into several different arguments to -f and -d, you need to put quotes $i. (So: [ -f "$i" ] and [ -d "$i" ]. In order to support directories with spaces in them, you should do the same for $1 where it is used.

Leave a Reply

Your email address will not be published. Required fields are marked *