1

I wrote a quick shell script to emulate the situation of xkcd #981 (without hard links, just symlinks to parent dirs) and used a recursive function to create all the directories. Unfortunately this script does not provide the desired result, so I think my understanding of the scope of variable $count is wrong.

How can I properly make the function use recursion to create twenty levels of folders, each containing 3 folders (3^20 folders, ending in soft links back to the top)?

#!/bin/bash
echo "Generating folders:"
toplevel=$PWD
count=1
GEN_DIRS() {
for i in 1 2 3
do
        dirname=$RANDOM
        mkdir $dirname
        cd $dirname
        count=$(expr $count + 1)
        if [ $count < 20 ] ; then
                GEN_DIRS
        else
                ln -s $toplevel "./$dirname"
        fi
done
}
GEN_DIRS
exit
0

2 Answers 2

2

Try this (amended version of the script) — it seems to work for me. I decline to test to 20 levels deep, though; at 8 levels deep, each of the three top-level directories occupies some 50 MB on a Mac file system.

#!/bin/bash
echo "Generating folders:"
toplevel=$PWD
GEN_DIRS()
{
    cur=${1:?}
    max=${2:?}
    for i in 1 2 3
    do
        dirname=$RANDOM
        if [ $cur -le $max ]
        then
            (
            echo "Directory: $PWD/$dirname"
            mkdir $dirname
            cd $dirname
            GEN_DIRS $((cur+1)) $max
            )
        else
            echo "Symlink:   $PWD/$dirname"
            ln -s $toplevel "./$dirname"
        fi
    done
}

GEN_DIRS 1 ${1:-4}

Lines 6 and 7 are giving names to the positional parameters ($1 and $2) passed to the function — the ${1:?} notation simply means that if you omit to pass a parameter $1, you get an error message from the shell (or sub-shell) and it exits.

The parentheses on their own (lines 13 and 18 above) mean that the commands in between are run in a sub-shell, so changes in directory inside the sub-shell do not affect the parent shell.

The condition on line 11 now uses arithmetic (-le) instead of string < comparisons; this works better for deep nesting (because the < is a lexicographic comparison, so level 9 is not less than level 10). It also means that the [ command is OK to use instead of the [[ command (although [[ would also work, I prefer the old-fashioned notation).

Sign up to request clarification or add additional context in comments.

4 Comments

Lines six/seven are setting cur and max to one and four I take it? Also, why the paren's around 14-19? Great solution, thanks!
Unfortunately, not creating all dirs right now. When called with 1 and 20, it creates 4 levels deep, and only one symlink in the final dir instead of 3 :/
The 4 levels was hard-coded in the call to GEN_DIRS. The 1 vs 3 symlinks was a misunderstanding of the requirement. Both issues are now fixed. With the script called gendirs.sh, running bash gendirs.sh creates 4 levels, and bash gendirs.sh 8 generates 8 levels, etc.
Best answer because it works, I understand it, and I've learned something from it. Thanks!
0

I end up creating a script like this:

#!/bin/bash

echo "Generating folders:"

toplevel=$PWD

level=0
maxlevel=4

function generate_dirs {
    pushd "$1" >/dev/null || return
    (( ++level ))
    for i in 1 2 3; do
        dirname=$RANDOM
        if (( level < maxlevel )); then
            echo "$PWD/$dirname"
            mkdir "$dirname" && generate_dirs "$dirname"
        else
            echo "$PWD/$dirname (link to top)"
            ln -sf "$toplevel" "$dirname"
        fi
    done
    popd >/dev/null 
    (( --level ))
}

generate_dirs .

exit

3 Comments

Works perfectly, but I can't say I understand it all. Can you explain line 11? You are deleting the argument you called the function with? And then why the last two lines of the function?
Also, twenty would take a lot longer than I thought :P
@CliffordSR See help pushd for more info. ` >/dev/null` sends output to null to make it quiet. And || return ends the function if it fails to change to that directory. The argument is a directory. The code is safe. If something fails for unknown cause it wouldn't go on an infinite loop or create stuffs on a directory that is not supposed to be it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.