6

I'm writing a script to get all files within a folder(including sub-folder):

#!/bin/bash
function loop() {
files=`ls -1Fd $1`
echo "$files" |
while IFS= read -r file; do
    if [[ "$file" == */ ]]; then
        loop "$file*"
    else
        echo "$file"
    fi
done
}
loop "$PWD/*"

I tried to test the script in this way:

#create folders and files
mkdir test\ folder && mkdir test\ folder/test\ subfolder && touch test\ folder/test\ subfolder/test\ file && cd test\ folder

#execute the script
~/path_to_the_script/test.sh

But it doesn't work, here is the error:

ls: cannot access /home/user/Documents/test: No such file or directory
ls: cannot access folder/*: No such file or directory

How to modify the script to achieve it?

2
  • I sort of don't understand what you are trying to do. Listing files in directory and subdirectory can be done with find . -type f already Sep 27, 2015 at 8:04
  • @Serg Yes, you're right. But I'm learning linux script, and I want to know how to do this with a script.
    – Searene
    Sep 27, 2015 at 9:12

2 Answers 2

6

First of all, don't parse ls. Now, the reason your script fails is because you are passing "$PWD/*". Because that's qupted, it will be expanded before it's passed to your function to /path/to/dir/* and since there is no file named * in your PWD, it fails.

However, even if it worked, that would have gotten you into an infinite loop.

What you are looking for is:

#!/bin/bash

function loop() {
    ## Do nothing if * doesn't match anything.
    ## This is needed for empty directories, otherwise
    ## "foo/*" expands to the literal string "foo/*"/
    shopt -s nullglob

    for file in $1
    do
    ## If $file is a directory
    if [ -d "$file" ]
    then
            echo "looping for $file"
        loop "$file/*"
    else
            echo "$file"
    fi
    done
}
loop "$PWD/*"

That, however, will fail if your PWD contains any whitespace characters. A safer way is:

#!/bin/bash

function loop() {
    ## Do nothing if * doesn't match anything.
    ## This is needed for empty directories, otherwise
    ## "foo/*" expands to the literal string "foo/*"/
    shopt -s nullglob

    ## Make ** recurse into subdirectories
    shopt -s globstar

    for file in "$@"/**
    do
    ## If $file is a file
    if [ -f "$file" ]
    then
        echo "$file"
    fi
    done
}
loop "$PWD"
1
  • +1 for shopt -s nullglob!
    – gavenkoa
    Jun 3, 2016 at 13:08
4

Why are you going so much out of way, just keep it simple

#!/bin/bash
function loop() { 
    for i in "$1"/*
    do
        if [ -d "$i" ]; then
            loop "$i"
        elif [ -e "$i" ]; then
            echo $i
        else
            echo "$i"" - Folder Empty"
        fi
    done
}
loop "$PWD"

Hope that helps ;)

3
  • Thank you. But I'm confused. I think for i in "$1"/* loops according to the value of IFS, and the default IFS contains white space, so if the file name contains white space, $1 will be split into multiple parts. For example, if "$PWD" is test folder, $i should be test and folder. But in fact, this is not the case. the script works very well. Why? Did I miss anything?
    – Searene
    Sep 27, 2015 at 13:17
  • Its the quotes, when you put $i within quotes like this "$i" it encapsulates the full path including white spaces. Try putting an echo before and after and using it with and without quotes , that will clarify it. Hope that helps ;)
    – H. Freeze
    Sep 27, 2015 at 23:17
  • @MarkZar To add more, same will be the case with "$1"/*, if you use $1/* instead of it you will get same error you mentioned.
    – H. Freeze
    Sep 27, 2015 at 23:29

Not the answer you're looking for? Browse other questions tagged or ask your own question.