stew
stew

Reputation: 22370

format bash variable for command

From my bash shell I would like to call a program n times with a different numbered parameter, which has to be in a fixed format like "%02i"

One way would be:

for ((i=23; i<42;i++)); do 
    sh ../myprogram `printf "%02i\n" $i`
done

Is there a way to improve the printf.. part? I believe this might be a performance bottleneck with more files and makes the line less readable than a built-in function (especially when condensed to a one-liner).

Upvotes: 5

Views: 27912

Answers (5)

mpoesse
mpoesse

Reputation: 1

try:

seq -w 23 41 

This will fillup with leading 0 automatically to needed size

or:

seq -f "%02g" 1 29

This will format to exact 2 digits.

So complete command would be:

for i in `seq -f "%02g" 23 41`; do 
    sh ../myprogram $i
done

Upvotes: -1

Commodore Jaeger
Commodore Jaeger

Reputation: 33370

In Bash, printf is provided by the shell (see the bash(1) manpage, search for "printf"), so you don't even have the (minimal) overhead of fork() and exec() to execute a separate command -- unless you run it from within backticks. Bash's built-in printf lets you assign the output to a given variable with the -v argument; your script fragment could be rewritten as:

for ((i=23; i<42;i++)); do 
    printf -v ii "%02i\n" $i
    sh ../myprogram $ii
done

I'm not sure I'd consider that more readable than the original.

The most resource-intensive part of your script fragment is calling your other shell script; I wouldn't worry about the overhead of calling printf unless further evidence indicates otherwise.

edited to add the difference between backticks and direct execution

Upvotes: 20

Johannes Schaub - litb
Johannes Schaub - litb

Reputation: 506905

Your number already has 2 decimal places. Why do you need to use printf then? If i remember correctly (haven't got a shell for testing here), it just pads the number up to 2 decimals when used with those flags. Personally, i like xargs:

seq 23 42 | xargs -n1 sh ../myprogram 

You can use the -w argument for seq, which pads the numbers with zeros if necessary, so they have all the same width.

It turns out seq is linux specific. Thanks for Dave in the comments for figuring it out (his answer). Use printf directly, without a loop:

printf '%02i\n' {23..42} | xargs -n1 sh ../myprogram 

I like to use xargs, because it allows easily running your commands in parallel up to some limit, can pass more than one number at once and allows other flexible options. Like Dave, i recommend you to drop the sh from it, and place it into your shell script, as first line instead:

#!/bin/sh
.....

Then just execute your stuff as

printf '%02i\n' {23..42} | xargs -n1 ../myprogram

This is more generic, and allows your script also to be called by the exec C library calls (at least in Linux, that's the case).

Upvotes: 5

Dave C
Dave C

Reputation: 7878

FYI: A completely different solution:

jot -w "%02i" - 23 42 | xargs -n 1 ../myprogram

This has the performance downside of calling jot (standard since 4.2BSD so all the BSD derivatives have it, but a quick look shows that this basic wonderful tool appears to be lacking from the Linux distributions I looked at). Edit: Linux (at least Red Hat) appears to have a subset of jot's features in a command called seq (thanks to litb's answer for this info).

This has the benefit of working in non-bash shells as well (unlike what some people think, there is more than one shell in active use and shell-agnostic solutions are generally a good idea).

Upvotes: 1

Paul Tomblin
Paul Tomblin

Reputation: 182792

How about

for i in {23..42} ; do
    sh ../myprogram $i
done

Numbers between 23 and 42 are always going to be in %02i format.

If you absolutely must format, then

for i in {23..42} ; do
    printf "%02i\n" | xargs -n1 sh ../myprogram $i
done

Substitutes the overhead of spawning xargs for the overhead of spawning the subshell for the backticks. I have no idea which is more efficient.

Upvotes: 0

Related Questions