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
2

3 Answers 3

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.

2
  • 1
    ...until you have filenames with tabs or newlines in them...
    – Jeff Schaller
    Aug 9, 2018 at 14:33
  • ... and then you modify IFS accordingly.
    – Szczad
    Aug 10, 2018 at 7:37
1

In bash:

shopt -s globstar
ls -1d **/
8
  • thanks for replaying but indeed that didn't help :( Aug 9, 2018 at 13:17
  • It lists all the directories and subdirectories from your current location, doesn't it?
    – Jeff Schaller
    Aug 9, 2018 at 13:18
  • yup it is! how can i presint them at the same line at a template of <directory><subdirectory> (newline )<directory><subdirectory> and dont let him spit the spaces to two lines Aug 9, 2018 at 13:21
  • It prints them one per line, just like your script seems to be trying to do...?
    – Jeff Schaller
    Aug 9, 2018 at 13:23
  • as an example I have directory include file with name "r1 August.request" I'm looking for output like "r1 August.request" but I'm getting : "r1 August.request" Aug 9, 2018 at 13:26
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?".

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .