0

dubdub_count=3

for (( i=1; i<=${dubdub_count}; i++ )); do
  my_apps --ammount=${dubdub_count}
done

The program will return the IP Address Example results

app1=10.10.10.1
app2=10.10.10.2
app3=10.10.10.3

Theses ip addresses will be used in other service. I am creating a apiserver.sh bash script to run apiserver command like this bellow

/usr/local/bin/apiserver --dubdub=http://${app1},http://${app2},http://{app3} --master

I want my apiserver.sh has ability to add dynamic results from my_apps results.

If i change dubdub_count=3 to be dubdub_count=4 My script must be able to be run like this

/usr/local/bin/apiserver --dubdub=http://10.10.10.1:8080,http://10.10.10.2:8080,http://10.10.10.3,http://10.10.10.4 --master

If i change dubdub_count=3 to be dubdub_count=5 My script must be able to be run like this

/usr/local/bin/apiserver --dubdub=http://10.10.10.1:8080,http://10.10.10.2:8080,http://10.10.10.3:8080,http://10.10.10.4:8080,http://10.10.10.5:8080 --master

Summary issue is how to add my_app results automatically into --dubdub parameters ? If my_app returns 6 IP Address, my --dubdub will get 6 IP Address too. I don't need to edit my bash script manually.

I have read dynamic var in bash as keyword in stackoverflow, still i can't find out the best solution for that. Should i suggest the my_apps developer to change the results method ?

2
  • Sorry, I don't really get where is the issue for you. At which part are you failing?
    – pLumo
    Sep 27, 2019 at 6:01
  • @pLumo added some explanation. Sorry...
    – 3-commerce
    Sep 27, 2019 at 6:09

3 Answers 3

3

You can add http:// and :8080 using sed, then paste -sd, to put them together separated by comma:

urllist=$(
  for ... done \
  | cut -d= -f2 \
  | sed 's#.*#http://&:8080#' \
  | paste -sd, -
) # output: http://10.10.10.1:8080,http://10.10.10.2:8080,http://10.10.10.3:8080
/usr/local/bin/apiserver --dubdub="$urllist" --master

Or do the cut, sed and paste part all together in one awk command:

urllist=$(
  for ... done \
  | awk -F= 'NR>1{printf ","};{printf "http://"$2":8080"}'
) # output: http://10.10.10.1:8080,http://10.10.10.2:8080,http://10.10.10.3:8080
/usr/local/bin/apiserver --dubdub="$iplist" --master
2
  • The first answer is more legit... but yeah... i think that regex is so perfect.
    – 3-commerce
    Sep 27, 2019 at 6:31
  • Do you know the right title for this question ? tell me some suggestion pls :)
    – 3-commerce
    Sep 27, 2019 at 6:32
2

Starting with:

$ seq -f "http://10.10.10.%g:8080" 1 3
http://10.10.10.1:8080
http://10.10.10.2:8080
http://10.10.10.3:8080

You can then turn that into a function:

$ ddcount() { seq -f "http://10.10.10.%g:8080" "$@" ; }
$ ddcount 5 7
http://10.10.10.5:8080
http://10.10.10.6:8080
http://10.10.10.7:8080

(note: because this just passes all arguments to the seq command, the FIRST argument is optional and defaults to 1. i.e. seq 3 is the same as seq 1 3, and ddcount 3 is the same as ddcount 1 3. You can also specify an increment if one is needed. See man seq for details)

Combine this with another function to join arguments using a delimiter (e.g. a comma):

$ join_by() { local d=$1; shift; printf '%s' "$1"; shift; printf '%s' "${@/#/$d}"; }

This is modelled on the perl join() function, but named join_by so it doesn't conflict with the join command. The first argument is the delimiter. Remaining arguments are the data to be joined.

$ join_by , $(ddcount 1 3)
http://10.10.10.1:8080,http://10.10.10.2:8080,http://10.10.10.3:8080

Note: Do not double-quote the $(ddcount 1 3) function call - you want the string it returns to be word-split into multiple items, not treated as just one long string. Compare the output of the above command with join_by , "$(ddcount 1 3)" to see the difference.

Finally, putting all of that together:

start=1; stop=3
/usr/local/bin/apiserver --dubdub="$(join_by , $(ddcount "$start" "$stop"))" --master

or

dd="$(join_by , $(ddcount "$start" "$stop"))"
/usr/local/bin/apiserver --dubdub="$dd" --master
3
  • Why not simply: http_format=$(seq -s, -f "http://10.10.10.%g:8080" 1 3) instead of a comparatively complicated join function ? (On bash v5.0.9 here.) -- Also I would not advise anyone to use dd as a variable name. Something "unfortunate" could happen.
    – Cbhihe
    Sep 27, 2019 at 16:57
  • because a) i wanted to write an answer that showed how to break down a problem into smaller tasks and solve them individually, and b) because a join_by function is extremely useful and everyone should know how to write one.
    – cas
    Sep 28, 2019 at 1:15
  • I don't have any superstitions about variable names.
    – cas
    Sep 28, 2019 at 1:16
2

Assuming that you can put that first loop into its own script or into a shell function called my_apps_loop (or you can just insert it in place for the my_apps_loop call below).

#!/bin/bash

urls=()

# Create URLs
while IFS='=' read -r app ip; do
    urls+=( "http://$ip:8080" )
done < <(my_apps_loop)

(
    IFS=','

    # Call apiserver with generated URLs
    /usr/local/bin/apiserver --dubdub="${urls[*]}" --master
)

This would loop over the output of my_apps_loop (which would output the lines with app1=... etc.) reading the appN part into $app and the string after the = into $ip.

For each line read, the $ip bit is prepended with the string http://, suffixed with :8080, and inserted into the array urls.

The subshell at the end calls apiserver. The --dubdub option gets its value from the urls array with the values of the array concatenated into a single string delimited by commas.

4
  • All good but OP was edited to add port 8080 to every IP address output. Also why the parentheses as in ( IFS=',' [...]), in the last part of your script ? You cannot mean to execute the parentheses' content in a subshell (would not work as is anyway), nor is there any precedence in a list of operations at stake here ...
    – Cbhihe
    Sep 27, 2019 at 17:36
  • @Cbhihe Thanks for the heads up about the port (now fixed). The parentheses at the end limits the scope of setting IFS to a comma to only the commands within that subshell. This is used to delimit the values in urls. What would not work?
    – Kusalananda
    Sep 27, 2019 at 18:32
  • Just a syntax issue for me... I was under the impression that subshells in bash are created with following syntax (list) when using parentheses (no space, no tabs after opening or before closing). So I would favor either (IFS=','; /usr/local/....) or even IFS=',' && /usr/local/... with no parentheses since the latter (branching conditionned on process exit status) does create a subshell in which you can set IFS independently of your calling env..
    – Cbhihe
    Sep 28, 2019 at 4:23
  • 1
    @Cbhihe The newlines don't matter, and I wanted to add a comment before the actual command. Using IFS=',' && command does not start a subshell and will not limit the scope of IFS in any way, so it would set IFS for the rest of the script, which may not be wanted.
    – Kusalananda
    Sep 28, 2019 at 6:56

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .