0

I'm trying to print all subdirectories, using the function below but for some reason when the file name include spaces (" "), the echo splits the name into two parts. Here is an example:

My directory includes a file called r1 August.request.

The output is:

r1

August.request

and I'm looking for

r1     August.request

Here is my script:

#!/bin/bash

function ScanFile {
    for file in `ls` ; do  
    if [[ -d "$file" ]] ; then
            cd "$file"
            ScanFile
            cd ..
            else
            echo "$file"
    fi
    done
} 
ScanFile
lgeorget
  • 13,914

3 Answers3

2

There is so called IFS variable in SH compliant shells. IFS stands for Internal Field Separator. This variable controls how the shell detects parameter boundaries like in positional arguments.

By default it is:

set IFS=$' \t\n'

Which translates to: split the input into tokens separated by space, tab and new line.

Your files consists of white spaces. Try doing it this way:

OLD_IF="$IFS"
IFS=$'\t\n'
for file in `ls -1` ; do
     # Content of your loop
done
IFS="$OLD_IFS"

Here the catch is to store previous IFS value and restore it.

Edit: added ls -1 as this lists one file per line hence does the stuff you've mentioned.

Szczad
  • 161
1

In bash:

shopt -s globstar
ls -1d **/
Jeff Schaller
  • 67,283
  • 35
  • 116
  • 255
0

Your function parses the unquoted output of ls. This means that the shell will perform word splitting (by default on any whitespace) and filename generation (globbing) on the output of ls.

Instead:

dirwalk () {
    indent=${1:-0}

    for name in *; do
        [ ! -e "$name" ] && continue
        if [ -d "$name" ]; then
            printf '%*sDir: "%s"\n' "$indent" "" "$name"
            ( cd "$name" && dirwalk "$(( indent + 4 ))" )
        else
            printf '%*sFile: "%s"\n' "$indent" "" "$name"
        fi
    done
}

or, without the fancy indentation,

dirwalk () {
    for name in *; do
        [ ! -e "$name" ] && continue
        if [ -d "$name" ]; then
            printf 'Dir: "%s"\n' "$name"
            ( cd "$name" && dirwalk )
        else
            printf 'File: "%s"\n' "$name"
        fi
    done
}

By using * instead of the output of ls, we generate a properly delimited list of filenames to iterate over in the current directory, even if these contain spaces.

The -e test is to make sure that the thing that we iterate over actually exists. If we enter an empty directory, the * would not expand and would remain as *. In bash, you may set the nullglob shell option (with shopt -s nullglob) to make unmatched patterns expand to nothing instead.

By using cd in a subshell (within parentheses), we don't have to remember to cd .. to get back to the previous directory. The environment outside of ( ... ) would not be affected by the cd inside it.

Using printf rather than echo we have greater control over the formatting of the output. Also see "Why is printf better than echo?".

Kusalananda
  • 333,661