3

Currently learning some bash scripting and having an issue with a question involving listing all files in a given directory and stating if they are a file or directory. The issue I am having is that I only get either my current directory or if a specify a directory it will just say that it is a directory eg. /home/user/shell_scripts will return shell_scipts is a directory rather than the files contained within it. This is what I have so far:

dir=$dir
for file in $dir; do
    if [[ -d $file ]]; then
        echo "$file is a directory"
    if [[ -f $file ]]; then
        echo "$file is a regular file"
    fi
done
Ant695
  • 71
  • 1
  • 2
  • 10
  • Possible duplicate of [How to get the list of files in a directory in a shell script?](https://stackoverflow.com/q/2437452/608639), [How to list files in directory using bash?](https://stackoverflow.com/q/7265272/608639), [Shell script print out file names when given a folder](https://stackoverflow.com/q/45705896/608639), [Show files in current directory using Git Bash?](https://stackoverflow.com/q/28738331/608639), etc. – jww May 30 '18 at 01:06

5 Answers5

8

Your line:

for file in $dir; do

will expand $dir just to a single directory string. What you need to do is expand that to a list of files in the directory. You could do this using the following:

for file in "${dir}/"* ; do

This will expand the "${dir}/"* section into a name-only list of the current directory. As Biffen points out, this should guarantee that the file list wont end up with split partial file names in file if any of them contain whitespace.

If you want to recurse into the directories in dir then using find might be a better approach. Simply use:

for file in $( find ${dir} ); do

Note that while simple, this will not handle files or directories with spaces in them. Because of this, I would be tempted to drop the loop and generate the output in one go. This might be slightly different than what you want, but is likely to be easier to read and a lot more efficient, especially with large numbers of files. For example, To list all the directories:

find ${dir} -maxdepth 1 -type d

and to list the files:

find ${dir} -maxdepth 1 -type f

if you want to iterate into directories below, then remove the -maxdepth 1

Component 10
  • 10,247
  • 7
  • 47
  • 64
  • This might break, if there are really many files (command line too long). – user1934428 Apr 11 '16 at 14:27
  • Yes, it's _possible_ although I have used this approach many times for very large numbers of files (>10^5) and not had a problem. I would be tempted to ditch the `for`/`do`/`done` approach and directly use the `ls` or `find` command if possible which would be much more efficient - although I am trying to keep within the scope of the OPs question. I will update accordingly. – Component 10 Apr 11 '16 at 16:01
  • This won't work well with filenames that contain whitespace or other special characters. – Biffen Apr 12 '16 at 06:00
  • Certainly, I agree that the `for` examples won't. I've changed the first one to work a bit better. The straight `find` examples should work OK though. Of course, it all depends what the output will be used for. Generally, I've found that there are 101 places where files that contain whitespace or other special characters in their names will cause problems and for that reason I try to insist on changing _them_ if at all possible. – Component 10 Apr 12 '16 at 09:04
2
ls -F ~ | \
sed 's#.*/$#/& is a Directory#;t quit;s#.*#/& is a File#;:quit;s/[*/=>@|] / /'

The -F "classify" switch appends a "/" if a file is a directory. The sed code prints the desired message, then removes the suffix.

agc
  • 7,973
  • 2
  • 29
  • 50
  • 1
    This won't work with files that have newlines in their names. – Biffen Apr 12 '16 at 06:04
  • 1
    Thanks @Biffen, that's horrifying, filenames with newlines are a _thing_? Just looked them up now. Just made one. Just tested the above **sed** code -- which failed. OK, but who even _wanted_ newlines there, what's the history of this thing? I get the 'how', and seek the 'why'... – agc Apr 12 '16 at 09:38
  • 1
    I can't really answer the 'why?', except with a 'why not?'. ;) – Biffen Apr 12 '16 at 09:41
2

This is a good use for globbing:

for file in "$dir/"*
do
  [[ -d "$file" ]] && echo "$file is a directory"
  [[ -f "$file" ]] && echo "$file is a regular file"
done

This will work even if files in $dir have special characters in their names, such as spaces, asterisks and even newlines.

Also note that variables should be quoted ("$file"). But * must not be quoted. And I removed dir=$dir since it doesn't do anything (except break when $dir contains special characters).

Biffen
  • 6,249
  • 6
  • 28
  • 36
0
for file in $(ls $dir)
do
    [ -f $file ] && echo "$file is File"
    [ -d $file ] && echo "$file is Directory"
done

or replace the

$(ls $dir)

with

`ls $`
Enjoy Red
  • 9
  • 2
  • This won't work well with filenames that contain whitespace or other special characters. And what is `ls $` supposed to do?! – Biffen Apr 12 '16 at 06:00
0

If you want to list files that also start with . use:

for file in "${dir}/"* "${dir}/"/.[!.]* "${dir}/"/..?* ; do
TrevTheDev
  • 2,616
  • 2
  • 18
  • 36