0

I am using netcat to get a series of points which I would like to plot with gnuplot. However, I am having trouble passing the data to gnuplot without writing it to a file first.

In the simplest case, I want to plot a 3-point line that I've defined in a variable first as follows:

#!/bin/bash

DATA="10
12
15"

echo "$DATA"

# Data looks good in terminal (single vertical column of numbers 10, 12, 15)
# However, the following step doesn't work:

gnuplot -persist <<-EOFMarker
    plot $DATA w lines
EOFMarker

The actual use case is to get data from a networked Rigol oscilloscope and show it in a plot. Here is the full script so far:

#!/bin/bash
SERVER=192.168.0.12
PORT=5555

RIGOLDATA="$(echo ":TRAC:DATA? TRACE1" | netcat -q1 $SERVER $PORT | cut -c 13- | tr ',' '\n' | tr -d [:blank:])"
echo "$RIGOLDATA"

# RigolData looks good in terminal (single vertical column of numbers).
# However, the following does not work:

gnuplot -persist <<-EOFMarker
    plot $RIGOLDATA w lines
EOFMarker

I can get the desired result by passing the data into a temp file first as follows, but I am trying to avoid the use of a temp file:

#!/bin/bash
SERVER=192.168.0.12
PORT=5555

RIGOLDATA="$(echo ":TRAC:DATA? TRACE1" | netcat -q1 $SERVER $PORT | cut -c 13- | tr ',' '\n' | tr -d [:blank:])"

echo "$RIGOLDATA" > tempfile.csv


gnuplot -persist <<-EOFMarker
    plot "tempfile.csv" w lines
EOFMarker
4
  • Without knowing anything about gnuplot: How would you do it if you did pass the data through a file first? Or, did you check what plot $DATA w lines expands to, and does it look like the correct command to give?
    – ilkkachu
    Aug 22, 2023 at 18:27
  • Passing it through a temporary file works just fine. I added the code to show how.
    – Sebastian
    Aug 22, 2023 at 18:35
  • The quotes are needed for echo to show the newlines correctly. But in gnuplot, quotes or no quotes, it won't plot my variable, instead it keeps attempting one plot for each datapoint and errors out or something. I think I'm missing something how to expand or assign the variable appropriately inside gnuplot. Maybe someone could just get the first example with a statically defined 3-point list to work? Once that works, I can get the scope to work.
    – Sebastian
    Aug 22, 2023 at 18:53
  • @steeldriver, would it matter for gnuplot (I still don't know anything about it)? In any case those quotes wouldn't be the same: the ones in the echo prevent the shell from word-spltting and globbing, but quotes inside the here-doc would just get passed through as-is. (The here-doc is already a bit like a double-quoted string in that, i.e. cat <<EOF[newline]*[newline]EOF[newline] just prints *, it's globbed)
    – ilkkachu
    Aug 22, 2023 at 18:55

2 Answers 2

0
plot $DATA w lines

expands to

plot 10
12
15 w lines

i.e. whatever $DATA contains gets put there literally, newlines and all. That likely breaks the command. (I'm not sure if gnuplot has a way to give the data directly on the plot command line, but it'd probably need to be in some other format.)

The shell has process substitution that makes the output of a command available as a "file", and expands to the filename, but you might be better off passing the data through stdin, and giving the plot command to gnuplot as an option.

Based on the example command used here, something like this might work:

echo "$DATA" | gnuplot -e 'plot "-" w lines'

or rather by piping the data directly from where you're getting it, without collecting it in the shell in the first place:

echo ":TRAC:DATA? TRACE1" | netcat -q1 "$SERVER" "$PORT" | 
  cut -c 13- | tr ',' '\n' | tr -d "[:blank:]" |
  gnuplot -e 'plot "-" w lines'

Then again, I don't think using a temporary file is that bad. A lot of shell scripts use them, and so do non-shell programs, too. A lot of the programs commonly called from shell scripts can read files directly, while passing data from a shell variable requires more complications. (E.g. the pipeline echo "$var" | somecmd requires launching an extra copy of the shell to write the data, and here-docs and here-strings like somecmd <<< "$var" just might create a temporary file in background anyway...)

Of course you usually want to remove any temporary files when you're done, and you should make sure they're created with a unique name so there are no collisions. But that's not hard to do:

#!/bin/bash
server=192.168.0.12
port=5555
tempfile=$(mktemp)

echo ":TRAC:DATA? TRACE1" | netcat -q1 "$server" "$port" |
   cut -c 13- | tr ',' '\n' | tr -d "[:blank:]" > "$tempfile"


gnuplot -persist <<-EOFMarker
    plot "$tempfile" w lines
EOFMarker

rm -f -- "$tempfile"
4
  • Yet, using tempfiles comes with so many caveats and complications, most of which are not handled in your example (you're not checking the exit status of mktemp (a non-standard command), you're not removing the tempfile upon signal reception, you forgot the -- (yes the output of mktmp can start with -)), so I would still recommend not to use them. Here that temp file is not needed at all. You could at least use one the same way << does (or did historically, bash and other shells broke that) by using it deleted from the start. Aug 22, 2023 at 19:10
  • 1
    @StéphaneChazelas, yes, of course there is the implicit assumption that the temp file can be created (and that mktemp exists and works). Having temp files get left laying around after a process gets killed is not pretty, but it's also usually not much of a problem. Again, obviously IMO, as I said in the answer too. If that gnuplot -e doesn't work and commands to gnuplot would need to be given through stdin, I'd much rather put the data in a temp file than awkwardly arrange to pass it through another fd while passing something like /dev/fd/NN as the filename.
    – ilkkachu
    Aug 22, 2023 at 19:20
  • Using Korn-style <(...) or rc-style <{...} process substitution, or zsh's =(...) or fish's (...|psub -f) if you do need a tempfile removes the need to have to worry about tempfiles. Using tempfiles also means tasks need to be done one after the other rather than in parallel when using a pipe (with | or process substitution), which are even more reasons to want to try and avoid temp files. Aug 22, 2023 at 19:28
  • @StéphaneChazelas, One issue in this case is how the command in question seems to want the filename as part of a stream of instructions. In general, passing that process substitution pseudo-filename as part of another string doesn't seem at all easy: <(...) or =(...) aren't expanded inside a here-doc or a quoted string, and f=<(...) does expand but immediately closes the pipe so you can't use the filename later.
    – ilkkachu
    Aug 22, 2023 at 20:20
0

Ok I want to share the simple answer that I figured out, which is to install and use feedgnuplot.

This will plot a simple line connecting Y-coordinates 4, 8, 5, 7:

echo -e "4\n8\n5\n7" | feedgnuplot -w lines

This will plot a simple line connecting the (x,y) points (3,11), (4,15), (5,12):

echo -e "3 11\n4 15\n5 12" | feedgnuplot -w lines --domain

It is also possible to redraw the plot frequently for a live-view graph with the --stream option.

This is basically what I was looking for, and I am using feedgnuplot successfully now in a stable and quickly updating bash script for the Rigol DSA815 as follows:

while true; do
   printf "clear\n"
   echo ":TRAC:DATA? TRACE1" | netcat -N 192.168.0.12 5555 | cut -c 13- | tr ',' '\n' | tr -d [:blank:]
   printf "replot\n"
done | feedgnuplot --with 'lines lw 3' --stream 0 --exit --xlen 601

I hope this helps anyone trying to make a gnuplot graph without using a temp file!

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .