Home > database >  bash recursion automatically ends after single level
bash recursion automatically ends after single level

Time:06-16

Why is this make_request function ending just after a single traversal?

make_request(){
    path="${1//' '/' '}"
    echo $path
    mkdir -p $HOME/"$1"
    $(curl --output $HOME/"$1"/$FILE_NAME -v -X GET $BASE_URL"/"$API_METHOD"/"$path &> /dev/null)
    # sample response from curl
    # {
    #     "count":2,
    #     "items": [
    #         {"path": "somepath1", "type": "folder"},
    #         {"path": "somepath2", "type": "folder"},
    #     ]
    # }
    count=$(jq ".count" $HOME/"$1"/$FILE_NAME)
    for (( c=0; c<$count; c   ))
    do
        child=$(jq -r ".items[$c] .path" $HOME/"$1"/$FILE_NAME);
        fileType=$(jq -r ".items[$c] .type" $HOME/"$1"/$FILE_NAME);
        if [ "$fileType" == "folder" ]; then
            make_request "$child"
        fi
    done
}

make_request "/"

make_request "/" should give the following output:

/folder
/folder/folder1-1
/folder/folder1-1/folder1-2
/folder/foler2-1
/folder/folder2-1/folder2-2
/folder/folder2-1/folder2-3 ...

but I am getting the following:

/folder
/folder/folder1-1
/folder/folder1-1/folder1-2

CodePudding user response:

You are using global variables everywhere. Therefore, the inner call changes the loop variables c and count of the outer call, resulting in bogus.

Minimal example:

f() {
  this_is_global=$1
  echo "enter $1: $this_is_global"
  ((RANDOM%2)) && f "$(($1 1))"
  echo "exit $1: $this_is_global"
}

Running f 1 prints something like

enter 1: 1
enter 2: 2
enter 3: 3
exit 3: 3
exit 2: 3
exit 1: 3

Solution: Make the variables local by writing local count=$(...) and so on. For your loop, you have to put an additional statement local c above the for.

CodePudding user response:

As currently written all variables have global scope; this means that all function calls are overwriting and referencing the same set of variables, this in turn means that when a child function returns to the parent the parent will find its variables have been overwritten by the child, this in turn leads to the type of behavior seen here.

In this particular case the loop variable c leaves the last child process with a value of c=$count and all parent loops now see c=$count and thus exit; it actually gets a bit more interesting because count is also changing with each function call. The previous comment to add set -x (aka enable debug mode) before the first function call should show what's going on with each variable at each function call.

What OP wants to do is insure each function is working with a local copy of a variable. The easiest approach is to add a local ... <variable_list> at the top of the function, making sure to list all variables that should be treated as 'local', eg:

local path count c child fileType

CodePudding user response:

change variables to have local scope instead of global.

...
local count; # <------ VARIABLE MADE LOCAL
count=$(jq ".count" $HOME/"$1"/$FILE_NAME)
local c; # <------ VARIABLE MADE LOCAL
for (( c=0; c<$count; c   ))
do
    ....
done
...
   
  •  Tags:  
  • bash
  • Related