Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Shell recognizes files in ~ but not in ~/Documents

Tags:

linux

bash

shell

I'm taking a Unix class, and here's a part of my assignment:

For each file and subdirectory in the user’s ~/Documents directory, determine if the item is a file or directory, and display a message to that effect, using the file name in the statement.

So, what I have written is this:

docs=`ls ~/Documents`

for file in $docs ; do
    if [ -f $file ] ; then
        echo $file "is a file."
    elif [ -d $file ] ; then
        echo $file "is a directory."
    else
        echo $file "is not a file or directory."
    fi
done

My Documents directory includes these files and directories:

DocList.txt  (file)
Letter       (file)
mypasswdfile (file)
samples      (directory)
things       (directory)
touchfile    (file)

So I figured that the output should be this:

DocList.txt is a file.
Letter is a file.
mypasswdfile is a file.
samples is a directory.
things is a directory.
touchfile is a file.

However, this is the output:

DocList.txt is not a file or directory.
Letter is not a file or directory
mypasswdfile is not a file or directory
samples is not a file or directory
things is not a file or directory
touchfile is not a file or directory

I feel like I should mention that if I set the $docs variable to `ls ~' it will successfully display the contents of my home directory and whether the items are files or directories. This does not work with other paths I have tried.

like image 815
Alex Tockey Avatar asked Sep 29 '22 08:09

Alex Tockey


2 Answers

Your problem is that ls only outputs the file names without path.

So your $file gets the values

DocList.txt
Letter
mypasswdfile
samples
things
touchfile

from loop run to loop run.

If your current directory is NOT ~/Documents, testing these file names is wrong, as this would search in the current directory and not in the intended one.

A much better way to accomplish your task is

for file in ~/Documents/* ; do
    ...
done

which will set $file to each of the full path names needed to find your file.

After doing so, it should work, but it is very error prone: once your path or one of your files starts having a space or other blank character in it, it will fall on your feet.

Putting " around variables which can potentially contain something with a space etc. is quite essential. There is almost no reason ever to use a variable without its surrounding ".

What is the difference here?

With [ -f $file ], and file='something with spaces', [ is called with the arguments -f, something, with, spaces and ]. This surely leads to wrong behaviour.

OTOH, with [ -f "$file" ], and file='something with spaces', [ is called with the arguments -f, something with spaces and ].

So quoting is very essential in shell programming.

Of course, the same holds for [ -d "$file" ].

like image 163
glglgl Avatar answered Oct 02 '22 15:10

glglgl


The problem is your ls command - you're treating the output of ls as absolute e.g. /home/alex/Documents/DocList.txt, but when you do ls ~/Documents it prints out DocList.txt (a relative file path / name).

To get the expected absolute behaviour you can use the find command instead:

docs=`find ~/Documents`

As mentioned in the comments and in another answer, to also be able to handle whitespace in filenames you need to do something like:

docs=( ~/Documents/* )
for f in "${docs[@]}"; do
    ...
like image 21
cronburg Avatar answered Oct 02 '22 13:10

cronburg