Question

In a bunch of languages I tried, functions can return values in a fair amount of types (ints, strings, various objects, functions that return functions etc.), but in bash, it seems like the only type allowed is int, and it is not supposed to be used by other functions but by the shell itself to know if everything went OK (returning 0), or if some error occured, in which case the value returned is the error code.

So for example, suppose you want to write a function that want to return something to another function. How should you do it ?

Suppose you want to write a findvideos function that returns every video found in a particular folder (and its subfolders). You want to use the result of that function in another function, how would you do it ? via echo and pipes ?

I was stuck with while writing a small script like this one :

function firefoxcache {
    cache_dir=$1
    videos_dir=$2
    videos=$(findallvideos $videos_dir)
    for video in $videos; do
    echo cp $video $videos_dir/$(basename $video).flv
    done;
}

function findallvideos {
    videos=$(find $1 -exec file {} \; | grep -i $VIDEOS_REGX | cut -f1 -d:)
    #echo $videos
    return videos
}   

EDIT

With help of Stackers, here's the working code (minus the silly double slash problem):

#!/usr/bin/env bash
function firefoxcache {
cache_dir=$1
videos_dir=$2
echo "$cache_dir", "$videos_dir"
echo
for video in $(findallvideos $cache_dir); do
echo cp $video $videos_dir/$(basename $video).flv
cp $video $videos_dir/$(basename $video).flv
done;
}

function findallvideos {
#echo findallvideos "$1"
for video in $(find $1 -exec file {} \; | grep -i $VIDEOS_REGX | cut -f1 -d:); do
echo $video;
done;
}

firefoxcache $1 $2

# cp /home/chaouche/.cache/mozilla/firefox/qmkhe4mr.default/Cache/4/B6/6AB61d01 VIDEOS//6AB61d01.flv
# cp /home/chaouche/.cache/mozilla/firefox/qmkhe4mr.default/Cache/4/28/A3524d01 VIDEOS//A3524d01.flv
# cp /home/chaouche/.cache/mozilla/firefox/qmkhe4mr.default/Cache/4/D2/29D4Ad01 VIDEOS//29D4Ad01.flv
# cp /home/chaouche/.cache/mozilla/firefox/qmkhe4mr.default/Cache/4/A1/15D75d01 VIDEOS//15D75d01.flv
# cp /home/chaouche/.cache/mozilla/firefox/qmkhe4mr.default/Cache/4/A3/2C971d01 VIDEOS//2C971d01.flv
# cp /home/chaouche/.cache/mozilla/firefox/qmkhe4mr.default/Cache/4/7E/E17D8d01 VIDEOS//E17D8d01.flv
# cp /home/chaouche/.cache/mozilla/firefox/qmkhe4mr.default/Cache/4/AC/5295Dd01 VIDEOS//5295Dd01.flv
Was it helpful?

Solution 2

Shell scripts can only return integer values from function types. If you want string output from them you have to echo the string and then capture that output within a subshell assigned to a variable.

e.g.

function foo()
{
    if [ "$1" = "hello" ] ; then
        echo "world";
        return 0;
    fi
    return 1;
}

a=$(foo "hello"); # a = world
echo $?;  # prints 0 - return values and exit status's are always assigned here.

a=$(foo); # a = empty string
echo $?;  # prints 1.

OTHER TIPS

Bash (and other shells) are using streams to communicate values. The return value you refer to is rather related to exceptions than to return values. If a return value is ≠ 0 the semantics of this is typically that an error has occurred and the caller should not continue as if nothing has happened.

If, on the other hand, you want to return a value, the called function, script, subprocess, whatever should print the value-to-return to stdout. The caller can then capture that output and use it:

square() {
  echo $[ $1 * $1 ]
}

a=4
b=$(square $a)
echo "$a squared is $b"

stdout is the main stream to perform that; you can, however use other streams like stderr as well; so shells can "return" more than one value.

There are other aspects, of course, like subprocesses do not necessarily have to terminate for the father to read and act upon their output. But I think that's a little out of scope for your question.

Concerning your concrete usecase you have to find a solution for the age-old problem of how to transmit lists of values via a byte stream. One solution is to use the print0 option of the find Unix tool. It prints the file names with a terminating zero byte for each (which is one of the few characters which cannot occur in a Unix path). The reader process then has to expect this format of course. You can use use the option -0 in xargs for instance.

In your case, I would use a loop using read then:

function findallfiles {
    find "$1" -print0
}

function findallvideos {
    findallfiles | while IFS='' read -d $'\0' f
      do
        [[ "$(file -b "$f")" =~ "$VIDEOS_REGX" ]] && printf "%s\0" "$f"
      done
}

function firefoxcache {
  cache_dir=$1
  videos_dir=$2
  findallvideos "$videos_dir" | while IFS='' read -d $'\0' video
    do
      echo cp "$video" "$videos_dir/$(basename "$video").flv"
    done
}

So for example, suppose you want to write a function that want to return something to another function. How should you do it ?

By writing something on stdout from function let caller function capture the output.

Example:

function foo () {
    echo "hello"
}

function fooCaller() {
    ret=$(foo)
    [[ "$ret" == "hello" ]] && echo "hello returned from function foo()"
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top