简体   繁体   中英

Shell script: variable scope in functions

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

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).

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM