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 '19 at 6:01
  • @pLumo added some explanation. Sorry... – Nicky Puff Sep 27 '19 at 6:09
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. – Nicky Puff Sep 27 '19 at 6:31
  • Do you know the right title for this question ? tell me some suggestion pls :) – Nicky Puff Sep 27 '19 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 '19 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 '19 at 1:15
  • I don't have any superstitions about variable names. – cas Sep 28 '19 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 '19 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 '19 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 '19 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 '19 at 6:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.