1

I have a bash method that looks like this

function getServiceId() {
    serviceId=$(docker -H $1 ps -qf label=com.docker.swarm.service.name=$2)
    $3=serviceId
    return 0
}

I execute this function like this

getServiceId $node1 $service1 retVal

The error comes from line $3=serviceId. It tries to execute the the value of serviceId on bash thinking it is a bash command. My goal is to return the value in the variable serviceId. How do I do that?

curiousengineer
  • 2,196
  • 5
  • 40
  • 59

3 Answers3

-1

Simply capture the STDOUT of the getServiceId function.

function getServiceId() {
    docker -H "$1" ps -qf "label=com.docker.swarm.service.name=$2"
}

serviceId="$(getServiceId "arg1" "arg2")"
echo "Service ID is $serviceId"
-1

You don't return values from functions in shell; you write to standard output, or set global variables. (No one ever said shell promoted good software engineering practice.)

getServiceId () {
    docker -H "$1" ps -qf label=com.docker.swarm.service.name="$2"
}

serviceID=$(getServiceID)

or

getServiceID () {
    serviceID=$(docker -H "$1" ps -qf label=com.docker.swarm.service.name="$2")
}
chepner
  • 497,756
  • 71
  • 530
  • 681
-1

Try this:

function getServiceId() {
    local serviceId
    read serviceId  < <(
        docker -H $1 ps -qf label=com.docker.swarm.service.name=$2
    )
    printf -v $3 "%s" "$serviceId"
}

getServiceId $node1 $service1 retVal

This will work fine for this specific case. But I like to use read for setting variable for a lot of thing like:

{ read foo ; read device total use free prct mpoint } < <(df -k /)
echo $use

or

# Work on DB
while IFS=$'\t' read id name label ;do
    [ "$id" ] && (( id > 100 )) && {
         # Doing something for entries with id > 100 ...
    }
done < <(
    sqlclient "SELECT id,name,label FROM ..."
)

But care: by default read will interpret backslashes \ as escape char (wiping a newline, for example). see help read:

read: read [-ers] [-a array] [-d delim] [-i text] [-n nchars] [-N nchars] [-p prompt] [-t timeout] [-u fd] [name ...]
   Read a line from the standard input and split it into fields.

   Reads a single line from the standard input, or from file descriptor FD
   if the -u option is supplied.  The line is split into fields as with word
   splitting, and the first word is assigned to the first NAME, the second
   word to the second NAME, and so on, with any leftover words assigned to
   the last NAME.  Only the characters found in $IFS are recognized as word
   delimiters.

   If no NAMEs are supplied, the line read is stored in the REPLY variable.

   Options:
     -a array assign the words read to sequential indices of the array
          variable ARRAY, starting at zero
     -d delim continue until the first character of DELIM is read, rather
          than newline
     -e       use Readline to obtain the line in an interactive shell
     -i text  Use TEXT as the initial text for Readline
     -n nchars    return after reading NCHARS characters rather than waiting
          for a newline, but honor a delimiter if fewer than NCHARS
          characters are read before the delimiter
     -N nchars    return only after reading exactly NCHARS characters, unless
          EOF is encountered or read times out, ignoring any delimiter
     -p prompt    output the string PROMPT without a trailing newline before
          attempting to read
     -r       do not allow backslashes to escape any characters
     -s       do not echo input coming from a terminal
     -t timeout   time out and return failure if a complete line of input is
          not read within TIMEOUT seconds.  The value of the TMOUT
          variable is the default timeout.  TIMEOUT may be a
          fractional number.  If TIMEOUT is 0, read returns immediately,
          without trying to read any data, returning success only if
          input is available on the specified file descriptor.  The
          exit status is greater than 128 if the timeout is exceeded
     -u fd        read from file descriptor FD instead of the standard input

   Exit Status:
   The return code is zero, unless end-of-file is encountered, read times out
   (in which case it's greater than 128), a variable assignment error occurs,
   or an invalid file descriptor is supplied as the argument to -u.
F. Hauri - Give Up GitHub
  • 64,122
  • 17
  • 116
  • 137
  • yes that is a Q i also have, I simply did this and it works, is it sth incorrect to do? printf -v $3 "%s" $(docker -H $1 ps -qf label=com.docker.swarm.service.name=$2) – curiousengineer Apr 26 '17 at 21:06
  • @curiousengineer Quote the subshell but yes that is fine. – 123 Apr 26 '17 at 21:07
  • @123, what do u mean quote the subshell. Not getting it and what is the issue if i dont? – curiousengineer Apr 26 '17 at 21:08
  • @curiousengineer `"$(subshell stuff)"` The same thing it does if you quote variables, preserves a literal string. – 123 Apr 26 '17 at 21:11
  • Using `read` like this subjects the output of the process substitution to backslash interpretation and word splitting, i.e., resets all whitespace. Not sure what the benefit is supposed to be. – Benjamin W. Apr 26 '17 at 21:18
  • 1
    @F.Hauri So what's the benefit then? Why make it so it only works one specific case? – 123 Apr 26 '17 at 21:28
  • @123 I like to use `read` because of the very large potential you could obtain (upto binary read). See `while IFS=$'\t' read id name label ;do ... ;done < <(sql "SELECT id,name,label...")...` for sample – F. Hauri - Give Up GitHub Apr 26 '17 at 21:28
  • @F.Hauri Think somethings been lost in translation, I have no idea what `very large potential you could obtain` is supposed to mean. – 123 Apr 26 '17 at 21:36
  • @123 `read` let you interpret *escape* character (`\`) or not, could use *timeout*, set many different variables at once, and a lot of things... upto read binary. – F. Hauri - Give Up GitHub Apr 26 '17 at 21:46
  • 2
    @F.Hauri You aren't actually *using* any of those special features, and pointing them out has *no* relevance to the question as asked. Just use `serviceID=$(docker ...)`. – chepner Apr 26 '17 at 21:50
  • @chepner The syntax you propose *could suffice*, but learning *good practices* could be nice too! Syntax `read < <(...)` has quite same performance no more forks, is clearer and more scalable... And for the *relevance* I will let you re-read the **title** of this question. At all, I don't understand why my answer could be bad? – F. Hauri - Give Up GitHub Apr 26 '17 at 22:03