views:

2883

answers:

4

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).

+6  A: 

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

Commodore Jaeger
The printf is in backticks, which presumably means it needs to spawn a sub-shell to handle it.
Paul Tomblin
Ah, you're right. (A few quality minutes with strace proved that backticks spawn a child process.) Updating answer.
Commodore Jaeger
+1  A: 

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.

Paul Tomblin
Presumably that was an example (albeit perhaps a bad one).
Dave C
Actually, I only wrote that answer to show off the {n..m} list, which is probably more efficient that the for loop he posted.
Paul Tomblin
+3  A: 

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).

Johannes Schaub - litb
seq appears to be Linux specific. The BSD superset of seq is jot.
Dave C
+2  A: 

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).

Dave C