Using variables from shell script for gnuplot - bash

I try to plot several curves on a same plot using variables from a shell script. My problem is that I do not succeed in collecting the variables and use them in gnuplot.
Here is my code:
#!/bin/bash
for elem in AMD WALE SIM;
do
echo $elem
Utau_$elem=$(awk 'FNR==5{print $1}' file_$elem)
done
gnuplot -persist <<-EOFMarker
list= "AMD WALE SIM"
plot for [i in list] "stat_".i u 1:($1/Utau_.i) # title_i
EOFMarker
This script aim at avoiding to write all the plot sentences but should have the same effect that the following :
plot "stat_AMD" u 1:($1/Utau_AMD}) #title_AMD,\
"stat_WALE" u 1:($1/Utau_WALE}) #title_WALE,\
"stat_SIM" u 1:($1/Utau_SIM}) #title_SIM
I did not succeed in plotting anything and I am not sure that the link can be done between the value Utau_$elem for the bash script and Utau_.i from the gnuplot script. Does someone have an idea if it is possible and how can I code it ?
Thanks a lot !

Thank #markp-fuso, it works !
I just had to replace "($1/${Utau_fr[${i}]})" by "(column(1)/${Utau_fr[${i}]})" because the dollar signs are interpreted as starting a shell variable.

Related

gnuplot print multiple files with array

I need to plot multiple files in one plot with a bash script. I can manage this by stating the location of the files manually in plot function ie
plot "$outputdir/10/values.csv" using 1:2 with lines title "10", \
"$outputdir/20/values.csv" using 1:2 with lines title "20", \
"$outputdir/30/values.csv" using 1:2 with lines title "30"
This will print all three values files in one plot. But I have a dynamic array which changes depending on how many values it is suppose to get. So lets say the array arr looks like this instead
arr=(10 20 30 40)
#Sometimes arr has more values ex; arr=(10 20 30 40 50 60 70 80)
#arr corresponds to folders that will be created
#And will contain a values.csv file that I want to plot
Then the 40/values.csv will not be printed if I don't manually change the plot code
My current attempt to fix this is a for loop in gnuplot
#The array arr is dynamically generated in another function
#Varies in size as example above
ArrLength=${#arr[#]} #Get the length of array
plot for [p=0:$ArrLength] "$outputdir/${arr[$p]}/values.csv" using 1:2 with lines title "${arr[$p]}"
I don't get a error when plotting but in plot only one value is plotted and thats the first value in array ie it will only plot $outputdir/10/values.csv.
I tried setting p=0:4 to plot the first five files and still it only plotted the first file only. What is wrong with the for loop above?
Best regards
You seem to be mixing bash and gnuplot in a strange way. Using a bash script to try to generate a gnuplot script on the fly with inserted variables is a quick way to confuse yourself. It also makes it difficult to read and debug. It is easy to forget what bash is evaluating and what gnuplot is evaluating.
When I look at your first line
ArrLength=${#arr[#]} #Get the length of array
I can see that this is bash code because gnuplot would interpret a comment beginning with the first #. (This is also bash's syntax for arrays, not gnuplot's.) The dollar sign $ has a different meaning in gnuplot. Rather than mark a variable identifier, $ is a column number operator ($2 is column 2, $i is column i, etc.). So look at the line
plot for [p=0:$ArrLength] "$outputdir/${arr[$p]}/values.csv" using 1:2 with lines title "${arr[$p]}"
This is clearly a line of bash syntax, apparently inside a string trying to write a line of gnuplot. Bash will evaluate the variables $ArrLength, $outputdir, and ${arr[$p]}, and replace them with some string of their values. Also keep in mind that p is a variable in gnuplot, not a variable in bash. Bash will evaluate $p to something (an empty string if it has not been defined). You can't expect the gnuplot variable p to be used as the index in the bash evaluation of ${arr[$p]}, and then somehow result in a different string for each iteration of gnuplot's loop.
In short, what you have written is not gnuplot syntax, and it is really not a minimal and complete bash script either. It is not clear exactly how you intended bash and gnuplot to fit together like this, but it seems you have joined them too tightly.
My suggestion is to write a bash script and write a gnuplot script (as separate files). Gnuplot has its own flow control, iteration loops, and variable evaluation. You can write a self-contained gnuplot script for the general case of everything you need it to do, and then give it specifics on the command line from your bash script.
For example, it seems that your subdirectories are all multiples of 10, and always starting with 10. The only variable aspect is how many there are (what the last one is). Let's say this last value was somehow stored in a gnuplot variable last. Also, suppose we also somehow have the base output directory in outputdir:
(Inside the gnuplot script, named plot.gp):
plot for [p=10:last:10] sprintf("%s/%d/values.csv", outputdir, p) with lines title sprintf("%d", p)
The for [p=10:last:10] means to iterate from 10 through last (inclusive), adding 10 at each iteration. The first sprintf() function (like C) builds a string with the outputdir and p (both are variables in gnuplot). The using 1:2 is not necessary as the first two columns are the default columns to use with lines, but you can include them if you want to be explicit. The second sprintf() builds a title string from the iteration variable p.
Now, this assumes that outputdir and last have meaningful values. You can assign these values from your bash script when you invoke gnuplot on the command line:
(Inside the bash script, invoke the gnuplot script)
gnuplot -e "last=40" -e "outputdir=\"$outputdir\"" plot.gp
The -e option tells gnuplot to evaluate the given string before running the script in the file plot.gp. In this example, the gnuplot variable last will have the value 40 and the gnuplot variable outputdir will have whatever value bash evaluates $outputdir to be. Notice the escaped double quotes inside double quotes. The outer double quotes are to allow bash to evaluate variables inside the string ($outputdir needs to be evaluated by bash). The inner (escaped) quotes are to delimit the string within the gnuplot code. For example, if bash evaluates $outputdir to data, then gnuplot would see outputdir="data" which is a valid gnuplot assignment of a string to the variable outputdir. You could, if you want, combine these two -e options into one:
gnuplot -e "last=40;outputdir=\"$outputdir\"" plot.gp
You will likely want to use the value for last from your array in bash, rather than hard coding it like this. So in practice it may look more like
gnuplot -e "last=${arr[${#arr[#]}-1]};outputdir=\"$outputdir\"" plot.gp
Or, if you have bash 4.3 or later, you should be able to use a negative index:
gnuplot -e "last=${arr[-1]};outputdir=\"$outputdir\"" plot.gp
Notice that there are no escaped quotes around the use of the array variable. It is expected that it will evaluate to an integer (40, 90, etc.) and we want to assign last to an integer, not a string like outputdir.
If this one string seems complex, try thinking about the entire script like this. It would be easy to get confused as to what bash is doing and what gnuplot is doing.
In summary, write a bash script, and a separate gnuplot script. Gnuplot is capable of handling a general case. From bash, just give it some specifics on the fly, don't try to generate the entire script on the fly. It really does make things simpler.

Bash script for gnuplot

I have a c++ code that generates every time a variable number of ".txt" file.
I am using a bash script that loads the txt files and send them to a gnuplot script. Then I have to plot in the same figure always the same 2 columns of each of them (representing a altitude vs time plot).
My problem is that the x coordinate (1st column of txt files) does not finish always at the same value and, when the last value of x coordinate is not zero,I need to insert a cross in the gnuplot plot. Do you have an idea about how to do it?
This is how I am plotting the multiple files
plot for [object in objectnames] object.".txt" using 1:2 w l title object
where "objectnames" is a string with all the filenames sent by the BASH
Now, for plotting the CROSSES, I thought about two ways:
Send a array of coordinates to the gnuplot script and then set some label but GNUPLOT does not like arrays and I cannot create a variable number of label in this way.
The alternative is to read somehow the txt file inside the gnuplot script but I have no idea how to do it.
Thanks for the help
EDIT
BASH SCRIPT
export DATA_DIR=${1:-/home/../output}
export objectname
space=" "
OUT_FILES="$(find -L $DATA_DIR -name '*_Trajectory.txt')"
for file in $OUT_FILES; do
fig=$(basename "$file")
objectID=$(echo $fig| cut -d'_' -f 1)
# filenames concatenation
objectname=$objectname$objectID$space
done
export figNameAltitude="${file/${fig}/altitudePlot.png}"
gnuplot -e "folder='${DATA_DIR}';objectnames='${objectname}';figName='${figNameAltitude}';lineWidth='${linewidth}'" altitudePlot.gp
GNUPLOT SCRIPT ( I avoid to insert all the lines regarding the title, axis and so on)
plot for [object in objectnames] folder.'/'.object."_Trajectory.txt" using 1:2 w l lw lineWidth title object
This might help
set title "Energy vs. Time for Sample Data"
set xlabel "Time"
set ylabel "Energy"
plot "d1.dat" with lines
pause -1 "Hit Enter to continue"
execute the script file as **$**gnuplot filename
click here for more details

Parametric gnuplot Scripts

When I try to write a shell script such as
gnuplot
cd '/home/cagirici/test'
plot "case1.test" with linespoints
and run it in sudo, the script stops after running gnuplot.
In each .test file, I have 12 columns. I won't plot them all at once but choose what to plot. For instance, if I choose to plot the success percentage, it should plot 1:2 and 1:8. But if I choose error percentage, it should plot 1:4 and 1:10 (it is basically i:(i+6).)
I need to write a scripts that takes two inputs: i) file name ii) what to plot.
Notice that I also want to choose the line type, point type and output shell. (if there is a configuration to set specific line and point types as default values, please tell me this as well)
Can I write this script in Ubuntu? Or should I use a different method.
My script would look something like
case "$2" in
("success percentage") plot "$1" using 1:2, plot "$1" using 1:8
("error percentage") plot "$1" using 1:4, plot "$1" using 1:10
You can make this work by creating a Gnuplot script with variables and then passing values to these variables at run time from command line or your bash script.
Lets call the Gnuplot script Myscript.gp:
set terminal pngcairo
set output 'graph.png'
plot file u x:y w linespoints
Here file and x:y can be varried when you invoke Gnuplot from your bash script. A possible call to Myscript.gp is as follows:
gnuplot -e "x=1;y=2;file = \"mydata.dat\"" Myscript.gp
In the snippet of your bash script that you provide this may look like:
case "$2" in
("success percentage") gnuplot -e "x=1; y=2; file = \"$1\"" Myscript.gp
Below I'm also providing a dummy mydata.dat file which you can use to try things.
1.54 23.66 43.66
1.75 26.25 46.25
1.92 30.20 40.20
2.08 34.46 44.46
2.44 42.08 42.08
2.78 46.81 46.81
3.03 51.10 41.10
3.70 52.99 42.99
4.17 56.15 46.15
4.76 59.34 49.34
If you are calculating the column numbers (x and y) in your bash script then you can also use variables inside the plot command.
gnuplot -e "x=$x;y=$y;file = \"mydata.dat\"" Myscript.gp
Below I show the output once using:
gnuplot -e "x=1;y=2;file = \"mydata.dat\"" Myscript.gp
and once
gnuplot -e "x=1;y=3;file = \"mydata.dat\"" Myscript.gp
I had faced similar problem 2 years back, then my senior team members decided to write a tool for plotting graphs using a simple csv and plotting specifications and tool will plot graphs for you,
Initially it was designed for sar data on linux so named RSAR but in general it is a csv plotter.)
if required you can contact me nachiket.kate90#gmail.com

Gnuplot: One plot per file

I'm trying to plot the 1st and 3rd columns of multiple files, where each file is supposed to be plotted to an own output.png.
My files have the following names:
VIB2--135--398.6241
VIB2--136--408.3192
VIB2--137--411.3725
...
The first number in the file name is an integer, which ranges from 135-162. The second number is just a decimal number and there is no regular spacing between the values.
Basically I want to do something like this
plot for [a=135:162] 'VIB2--'.a.'--*' u 1:3 w l
although this doesn't work, of course, since the ' * ' is just the placeholder I know from bash and I don't know, if there is something similar in gnuplot.
Furthermore, each of the files should be, as already said above, plotted to its own output.png, where the two numbers should be in the output name, e.g. VIB2--135--398.6241.png.
I tried to come up with a bash script for this, like (edited):
#!/bin/bash
for file in *
do
gnuplot < $file
set xtics 1
set xtics rotate
set terminal png size 1920,1080 enhanced
set output $file.png
plot "$file" u 1:3 w l
done
but I still get
gnuplot> 1 14 -0.05
^
line 0: invalid command
gnuplot> 2 14 0.01
^
line 0: invalid command
...
which are actually the numbers from my input file. So gnuplot thinks, that the numbers I want to plot are commands... ?? Also, when the end of the file is reached, I get the following error message
#PLOT 1
plot: an unrecognized command `0x20' was encountered in the input
plot: the input file `VIB2--162--496.0271' could not be parsed
I've seen a few questions similar to mine, but the solutions didn't really work for me and I cannot add a comment, since I do not have the reputation.
Please help me with this.
gnuplot < $file starts gnuplot and feeds it the content of $file as input. That means gnuplot will now try to execute the commands in the data file which doesn't work.
What you want is a "here document":
gnuplot <<EOF
set xtics 1
set xtics rotate
set terminal png size 1920,1080 enhanced
set output $file.png
plot "$file" u 1:3 w l
EOF
What this does is: The shell reads the text up to the line with solemn EOF, replaces all variables, puts that into a temporary file and then starts gnuplot feeding it the temporary file as input.
Be careful that the file names don't contain spaces, or set output $file.png will not work. To be safe, you should probably use set output "$file.png" but my gnuplot is a bit rusty.

Shell script that passes parameter to gnuplot does not export the diagram (epslatex)

I have created a shell script (.run) that accepts the prefix for the names of the pictures as a parameter, and then calls gnuplot. However, for some reason, the picture is not saved. The code is:
#!/bin/sh
molecule=$1
echo "Plotting DFT-ADF PY results for $molecule"
echo "Tranmission plot (negatory SO)"
gnuplot -p << EOF
#!/usr/bin/gnuplot
set terminal epslatex size 5,3 color colortext
set output '$molecule_trans.tex'
plot cos(x) w l title 'cos(x)', sin(x) w l title 'sin(x)'
EOF
For my bachelor thesis I have to make several plots that are the same. Additionally, the computational cluster uses a qeueing system. For the purpose of being true to this system, I have created several shell scripts that automatically do stuff. In particular, about 45 simulations are called by the shell scripts, followed by a shell script that enters each simulations' directory and uses python files to evaluate the data into [.dat] files. Next, it should use a gnuplot file to make the graph. I use EPSLaTeX to make my figures, because it is so much nicer. However, in the current implementation this required me to manually edit the various latex files to rename the pictures.
In case you'll need more variables and do not want a $1, $2... mess:
You must use brackets around the variable name
set output '${molecule}_trans.tex'
because the underscore is a valid character for variable names, and bash looks for the variable $molecule_trans, see http://www.gnu.org/software/bash/manual/bashref.html#Shell-Parameter-Expansion.

Resources