Using shell 'printf' where the format string is in a variable and doesn't have a fixed number of field placeholders?

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP











up vote
0
down vote

favorite
1












I am writing a shell function which makes an external API call via cURL (the external API syntax isn't under my control). I've approached it like this (simplified):



#!/bin/sh

template_get_entry='get_entry:%s'
template_set_entry='set_entry:%s=%s'

curlheaders='-H stuff'
curluri="https://www.domain.com:1234/api.php"

# make an API call to get entry "foo"
call_api "$template_get_entry" "foo"

# make an API call to set entry "foo" to "bar"
call_api "$template_set_entry" "foo" "bar"

call_api()

apicmd="$( printf "$1" "$2" "$3" )"
result="$( eval "/usr/local/bin/curl" "$curlheaders" "-d" "$apicmd" "$curluri" )"
retcode="$?"

.....stuff.....




There are 2 problems with this code.



First, the number of args is variable. If the line defining apicmd is called with less than the maximal number of args, printf interprets any excess commands as extra instances of printing the format string, to be appended. I can't see the correct way to work around this.



Second, because I've used eval, this creates a knockon problem with eval, in that retcode will surely pick up the return code from eval and not from curl, and I don't know the right way to prevent/fix that.



How should I do something like this, which needs a variable number of args?










share|improve this question

















  • 1




    I don't really see why you use eval...
    – Kusalananda
    Aug 7 at 7:32










  • I don't know another way or a more sensible way to do variable-argument-count substitution, and run the result as a command (yet). I might, by the end of this question.
    – Stilez
    Aug 7 at 10:03











  • You don't seem to have a variable number of arguments in the call to curl though.
    – Kusalananda
    Aug 7 at 10:06










  • I don't. The curl arg string is templated, though, and the args used to build it are passed as the functio's 2nd+ args, substituted in the functions 1st arg, and the resulting string is used as the curl args. That's where the variable args list kicks in.
    – Stilez
    Aug 7 at 10:59










  • It's still just a simple string when you do the call to curl though.
    – Kusalananda
    Aug 7 at 11:03














up vote
0
down vote

favorite
1












I am writing a shell function which makes an external API call via cURL (the external API syntax isn't under my control). I've approached it like this (simplified):



#!/bin/sh

template_get_entry='get_entry:%s'
template_set_entry='set_entry:%s=%s'

curlheaders='-H stuff'
curluri="https://www.domain.com:1234/api.php"

# make an API call to get entry "foo"
call_api "$template_get_entry" "foo"

# make an API call to set entry "foo" to "bar"
call_api "$template_set_entry" "foo" "bar"

call_api()

apicmd="$( printf "$1" "$2" "$3" )"
result="$( eval "/usr/local/bin/curl" "$curlheaders" "-d" "$apicmd" "$curluri" )"
retcode="$?"

.....stuff.....




There are 2 problems with this code.



First, the number of args is variable. If the line defining apicmd is called with less than the maximal number of args, printf interprets any excess commands as extra instances of printing the format string, to be appended. I can't see the correct way to work around this.



Second, because I've used eval, this creates a knockon problem with eval, in that retcode will surely pick up the return code from eval and not from curl, and I don't know the right way to prevent/fix that.



How should I do something like this, which needs a variable number of args?










share|improve this question

















  • 1




    I don't really see why you use eval...
    – Kusalananda
    Aug 7 at 7:32










  • I don't know another way or a more sensible way to do variable-argument-count substitution, and run the result as a command (yet). I might, by the end of this question.
    – Stilez
    Aug 7 at 10:03











  • You don't seem to have a variable number of arguments in the call to curl though.
    – Kusalananda
    Aug 7 at 10:06










  • I don't. The curl arg string is templated, though, and the args used to build it are passed as the functio's 2nd+ args, substituted in the functions 1st arg, and the resulting string is used as the curl args. That's where the variable args list kicks in.
    – Stilez
    Aug 7 at 10:59










  • It's still just a simple string when you do the call to curl though.
    – Kusalananda
    Aug 7 at 11:03












up vote
0
down vote

favorite
1









up vote
0
down vote

favorite
1






1





I am writing a shell function which makes an external API call via cURL (the external API syntax isn't under my control). I've approached it like this (simplified):



#!/bin/sh

template_get_entry='get_entry:%s'
template_set_entry='set_entry:%s=%s'

curlheaders='-H stuff'
curluri="https://www.domain.com:1234/api.php"

# make an API call to get entry "foo"
call_api "$template_get_entry" "foo"

# make an API call to set entry "foo" to "bar"
call_api "$template_set_entry" "foo" "bar"

call_api()

apicmd="$( printf "$1" "$2" "$3" )"
result="$( eval "/usr/local/bin/curl" "$curlheaders" "-d" "$apicmd" "$curluri" )"
retcode="$?"

.....stuff.....




There are 2 problems with this code.



First, the number of args is variable. If the line defining apicmd is called with less than the maximal number of args, printf interprets any excess commands as extra instances of printing the format string, to be appended. I can't see the correct way to work around this.



Second, because I've used eval, this creates a knockon problem with eval, in that retcode will surely pick up the return code from eval and not from curl, and I don't know the right way to prevent/fix that.



How should I do something like this, which needs a variable number of args?










share|improve this question













I am writing a shell function which makes an external API call via cURL (the external API syntax isn't under my control). I've approached it like this (simplified):



#!/bin/sh

template_get_entry='get_entry:%s'
template_set_entry='set_entry:%s=%s'

curlheaders='-H stuff'
curluri="https://www.domain.com:1234/api.php"

# make an API call to get entry "foo"
call_api "$template_get_entry" "foo"

# make an API call to set entry "foo" to "bar"
call_api "$template_set_entry" "foo" "bar"

call_api()

apicmd="$( printf "$1" "$2" "$3" )"
result="$( eval "/usr/local/bin/curl" "$curlheaders" "-d" "$apicmd" "$curluri" )"
retcode="$?"

.....stuff.....




There are 2 problems with this code.



First, the number of args is variable. If the line defining apicmd is called with less than the maximal number of args, printf interprets any excess commands as extra instances of printing the format string, to be appended. I can't see the correct way to work around this.



Second, because I've used eval, this creates a knockon problem with eval, in that retcode will surely pick up the return code from eval and not from curl, and I don't know the right way to prevent/fix that.



How should I do something like this, which needs a variable number of args?







shell-script function variable-substitution printf return-status






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Aug 7 at 7:17









Stilez

441211




441211







  • 1




    I don't really see why you use eval...
    – Kusalananda
    Aug 7 at 7:32










  • I don't know another way or a more sensible way to do variable-argument-count substitution, and run the result as a command (yet). I might, by the end of this question.
    – Stilez
    Aug 7 at 10:03











  • You don't seem to have a variable number of arguments in the call to curl though.
    – Kusalananda
    Aug 7 at 10:06










  • I don't. The curl arg string is templated, though, and the args used to build it are passed as the functio's 2nd+ args, substituted in the functions 1st arg, and the resulting string is used as the curl args. That's where the variable args list kicks in.
    – Stilez
    Aug 7 at 10:59










  • It's still just a simple string when you do the call to curl though.
    – Kusalananda
    Aug 7 at 11:03












  • 1




    I don't really see why you use eval...
    – Kusalananda
    Aug 7 at 7:32










  • I don't know another way or a more sensible way to do variable-argument-count substitution, and run the result as a command (yet). I might, by the end of this question.
    – Stilez
    Aug 7 at 10:03











  • You don't seem to have a variable number of arguments in the call to curl though.
    – Kusalananda
    Aug 7 at 10:06










  • I don't. The curl arg string is templated, though, and the args used to build it are passed as the functio's 2nd+ args, substituted in the functions 1st arg, and the resulting string is used as the curl args. That's where the variable args list kicks in.
    – Stilez
    Aug 7 at 10:59










  • It's still just a simple string when you do the call to curl though.
    – Kusalananda
    Aug 7 at 11:03







1




1




I don't really see why you use eval...
– Kusalananda
Aug 7 at 7:32




I don't really see why you use eval...
– Kusalananda
Aug 7 at 7:32












I don't know another way or a more sensible way to do variable-argument-count substitution, and run the result as a command (yet). I might, by the end of this question.
– Stilez
Aug 7 at 10:03





I don't know another way or a more sensible way to do variable-argument-count substitution, and run the result as a command (yet). I might, by the end of this question.
– Stilez
Aug 7 at 10:03













You don't seem to have a variable number of arguments in the call to curl though.
– Kusalananda
Aug 7 at 10:06




You don't seem to have a variable number of arguments in the call to curl though.
– Kusalananda
Aug 7 at 10:06












I don't. The curl arg string is templated, though, and the args used to build it are passed as the functio's 2nd+ args, substituted in the functions 1st arg, and the resulting string is used as the curl args. That's where the variable args list kicks in.
– Stilez
Aug 7 at 10:59




I don't. The curl arg string is templated, though, and the args used to build it are passed as the functio's 2nd+ args, substituted in the functions 1st arg, and the resulting string is used as the curl args. That's where the variable args list kicks in.
– Stilez
Aug 7 at 10:59












It's still just a simple string when you do the call to curl though.
– Kusalananda
Aug 7 at 11:03




It's still just a simple string when you do the call to curl though.
– Kusalananda
Aug 7 at 11:03










2 Answers
2






active

oldest

votes

















up vote
1
down vote



accepted










You could try to fill the format strings with zero length specifiers up to the maximum expected parameter count:



template_get_entry='get_entry:%s %0.0s'





share|improve this answer




















  • Worked, and keeps code short. Note that if I didn't want to add null (zero length) field placeholders or didn't know the max args, I would have tried Kusalananda's solution.
    – Stilez
    Aug 8 at 11:13

















up vote
1
down vote













What you could do is to get the format string argument, shift it off the argument list and then use $@:



call_api () 
fmt=$1
shift

apicmd=$( printf "$fmt" "$@" )

# ...






share|improve this answer




















  • This sounds like it would work; however I ended up using RudiC's solution because it keeps the code short and if it worked would almost certainly be guaranteed portable, whereas shell substitutions and builtins might not always be. But I appreciate this answer which would have been my #2 choice to try, if it hadn't. Also this would be better code if I wasn't sure of a maximum number of args, or didn't want to pad out the template strings with extra null fields.
    – Stilez
    Aug 8 at 11:12











Your Answer







StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "106"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
convertImagesToLinks: false,
noModals: false,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);













 

draft saved


draft discarded


















StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f460980%2fusing-shell-printf-where-the-format-string-is-in-a-variable-and-doesnt-have-a%23new-answer', 'question_page');

);

Post as a guest






























2 Answers
2






active

oldest

votes








2 Answers
2






active

oldest

votes









active

oldest

votes






active

oldest

votes








up vote
1
down vote



accepted










You could try to fill the format strings with zero length specifiers up to the maximum expected parameter count:



template_get_entry='get_entry:%s %0.0s'





share|improve this answer




















  • Worked, and keeps code short. Note that if I didn't want to add null (zero length) field placeholders or didn't know the max args, I would have tried Kusalananda's solution.
    – Stilez
    Aug 8 at 11:13














up vote
1
down vote



accepted










You could try to fill the format strings with zero length specifiers up to the maximum expected parameter count:



template_get_entry='get_entry:%s %0.0s'





share|improve this answer




















  • Worked, and keeps code short. Note that if I didn't want to add null (zero length) field placeholders or didn't know the max args, I would have tried Kusalananda's solution.
    – Stilez
    Aug 8 at 11:13












up vote
1
down vote



accepted







up vote
1
down vote



accepted






You could try to fill the format strings with zero length specifiers up to the maximum expected parameter count:



template_get_entry='get_entry:%s %0.0s'





share|improve this answer












You could try to fill the format strings with zero length specifiers up to the maximum expected parameter count:



template_get_entry='get_entry:%s %0.0s'






share|improve this answer












share|improve this answer



share|improve this answer










answered Aug 7 at 7:47









RudiC

1,1837




1,1837











  • Worked, and keeps code short. Note that if I didn't want to add null (zero length) field placeholders or didn't know the max args, I would have tried Kusalananda's solution.
    – Stilez
    Aug 8 at 11:13
















  • Worked, and keeps code short. Note that if I didn't want to add null (zero length) field placeholders or didn't know the max args, I would have tried Kusalananda's solution.
    – Stilez
    Aug 8 at 11:13















Worked, and keeps code short. Note that if I didn't want to add null (zero length) field placeholders or didn't know the max args, I would have tried Kusalananda's solution.
– Stilez
Aug 8 at 11:13




Worked, and keeps code short. Note that if I didn't want to add null (zero length) field placeholders or didn't know the max args, I would have tried Kusalananda's solution.
– Stilez
Aug 8 at 11:13












up vote
1
down vote













What you could do is to get the format string argument, shift it off the argument list and then use $@:



call_api () 
fmt=$1
shift

apicmd=$( printf "$fmt" "$@" )

# ...






share|improve this answer




















  • This sounds like it would work; however I ended up using RudiC's solution because it keeps the code short and if it worked would almost certainly be guaranteed portable, whereas shell substitutions and builtins might not always be. But I appreciate this answer which would have been my #2 choice to try, if it hadn't. Also this would be better code if I wasn't sure of a maximum number of args, or didn't want to pad out the template strings with extra null fields.
    – Stilez
    Aug 8 at 11:12















up vote
1
down vote













What you could do is to get the format string argument, shift it off the argument list and then use $@:



call_api () 
fmt=$1
shift

apicmd=$( printf "$fmt" "$@" )

# ...






share|improve this answer




















  • This sounds like it would work; however I ended up using RudiC's solution because it keeps the code short and if it worked would almost certainly be guaranteed portable, whereas shell substitutions and builtins might not always be. But I appreciate this answer which would have been my #2 choice to try, if it hadn't. Also this would be better code if I wasn't sure of a maximum number of args, or didn't want to pad out the template strings with extra null fields.
    – Stilez
    Aug 8 at 11:12













up vote
1
down vote










up vote
1
down vote









What you could do is to get the format string argument, shift it off the argument list and then use $@:



call_api () 
fmt=$1
shift

apicmd=$( printf "$fmt" "$@" )

# ...






share|improve this answer












What you could do is to get the format string argument, shift it off the argument list and then use $@:



call_api () 
fmt=$1
shift

apicmd=$( printf "$fmt" "$@" )

# ...







share|improve this answer












share|improve this answer



share|improve this answer










answered Aug 7 at 7:30









Kusalananda

106k14209327




106k14209327











  • This sounds like it would work; however I ended up using RudiC's solution because it keeps the code short and if it worked would almost certainly be guaranteed portable, whereas shell substitutions and builtins might not always be. But I appreciate this answer which would have been my #2 choice to try, if it hadn't. Also this would be better code if I wasn't sure of a maximum number of args, or didn't want to pad out the template strings with extra null fields.
    – Stilez
    Aug 8 at 11:12

















  • This sounds like it would work; however I ended up using RudiC's solution because it keeps the code short and if it worked would almost certainly be guaranteed portable, whereas shell substitutions and builtins might not always be. But I appreciate this answer which would have been my #2 choice to try, if it hadn't. Also this would be better code if I wasn't sure of a maximum number of args, or didn't want to pad out the template strings with extra null fields.
    – Stilez
    Aug 8 at 11:12
















This sounds like it would work; however I ended up using RudiC's solution because it keeps the code short and if it worked would almost certainly be guaranteed portable, whereas shell substitutions and builtins might not always be. But I appreciate this answer which would have been my #2 choice to try, if it hadn't. Also this would be better code if I wasn't sure of a maximum number of args, or didn't want to pad out the template strings with extra null fields.
– Stilez
Aug 8 at 11:12





This sounds like it would work; however I ended up using RudiC's solution because it keeps the code short and if it worked would almost certainly be guaranteed portable, whereas shell substitutions and builtins might not always be. But I appreciate this answer which would have been my #2 choice to try, if it hadn't. Also this would be better code if I wasn't sure of a maximum number of args, or didn't want to pad out the template strings with extra null fields.
– Stilez
Aug 8 at 11:12


















 

draft saved


draft discarded















































 


draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f460980%2fusing-shell-printf-where-the-format-string-is-in-a-variable-and-doesnt-have-a%23new-answer', 'question_page');

);

Post as a guest













































































Popular posts from this blog

How to check contact read email or not when send email to Individual?

Displaying single band from multi-band raster using QGIS

How many registers does an x86_64 CPU actually have?