Sometimes your commands print multiple values and you want to use them as different shell variables. Let’s describe the problem (shown in the box below) with an example (that you can reproduce without any external data).
With the commands below, we’ll first make a noisy (\(\sigma=5\)) image (\(100\times100\) pixels) using Arithmetic. Then, we’ll measure116 its mean and standard deviation using Statistics.
$ astarithmetic 100 100 2 makenew 5 mknoise-sigma -oimg.fits $ aststatistics img.fits --mean --std -3.10938611484039e-03 4.99607077069093e+00
THE PROBLEM: you want the first number printed above to be stored in a shell variable called |
The first thing that may come to mind is to run Statistics two times, and write the output into separate variables like below:
$ my_std=$(aststatistics img.fits --std) ## NOT SOLUTION! ## $ my_mean=$(aststatistics img.fits --mean) ## NOT SOLUTION! ##
But this is not a good solution because as img.fits becomes larger (more pixels), the time it takes for Statistics to simply load the data into memory can be significant. This will slow down your pipeline and besides wasting your time, it contributes to global warming (by spending energy on an un-necessary action; take this seriously because your pipeline may scale up to involve thousands of large datasets)! Furthermore, besides loading of the input data, Statistics (and Gnuastro in general) is designed to do multiple measurements in one pass over the data as much as possible (to further decrease Gnuastro’s carbon footprint). So when given --mean --std, it will measure both in one pass over the pixels (not two passes!). In other words, in this case, you get the two measurements for the cost of one.
How do you separate the values from the first aststatistics
command above?
One ugly way is to write the two-number output string into a single shell variable and then separate, or tokenize, the string with two subsequent commands like below:
$ meanstd=$(aststatistics img.fits --mean --std) ## NOT SOLUTION! ## $ my_mean=$(echo $meanstd | awk '{print $1}') ## NOT SOLUTION! ## $ my_std=$(echo $meanstd | awk '{print $2}') ## NOT SOLUTION! ##
Let’s review the solution (in more detail):
xargs
117 (extended arguments) which puts the two numbers it gets from the pipe, as arguments for printf
(formatted print; because printf
doesn’t take input from pipes).
printf
call, we write the values after putting a variable name and equal-sign, and in between them we put a ; (as if it was a shell command).
The %s
tells printf
to print each input as a string (not to interpret it as a number and loose precision).
Here is the output of this phase:
$ aststatistics img.fits --mean --std \ | xargs printf "my_mean=%s; my_std=%s" my_mean=-3.10938611484039e-03; my_std=4.99607077069093e+00
After the solution above, you will have the two my_mean
and my_std
variables to use separately in your pipeline:
$ echo $my_mean -3.10938611484039e-03 $ echo $my_std 4.99607077069093e+00
This eval
-based solution has been tested in in GNU Bash, Dash and Zsh and it works nicely in them (is “portable”).
This is because the constructs used here are pretty low-level (and widely available).
For examples usages of this technique, see the following sections: Extracting a single spectrum and plotting it and Pseudo narrow-band images.
The actual printed values by aststatistics
may slightly differ for you.
This is because of a different random number generator seed used in astarithmetic
.
To get an exactly reproducible result, see Generating random numbers
For more on xargs
, see https://en.wikipedia.org/wiki/Xargs.
It will take the standard input (from the pipe in this scenario) and put it as arguments of the next program (printf
in this scenario).
In other words, it is good for programs that don’t take input from standard input (printf
in this case; but also includes others like cp
, rm
, or echo
).
JavaScript license information
GNU Astronomy Utilities 0.23 manual, July 2024.