【问题标题】:gnuplot print multiple files with arraygnuplot 用数组打印多个文件
【发布时间】:2016-12-22 09:42:07
【问题描述】:

我需要使用 bash 脚本在一个绘图中绘制多个文件。我可以通过在绘图函数中手动说明文件的位置来管理这个,即

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"

这将在一个图中打印所有三个值文件。但是我有一个动态数组,它会根据它应该获得的值的数量而变化。所以让我们说数组 arr 看起来像这样

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

如果我不手动更改绘图代码,则不会打印 40/values.csv

我目前尝试解决这个问题是 gnuplot 中的一个 for 循环

#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]}"

绘图时我没有收到错误,但在绘图中只绘制了一个值,这是数组中的第一个值,即它只会绘制 $outputdir/10/values.csv。

我尝试设置 p=0:4 来绘制前五个文件,但它仍然只绘制了第一个文件。上面的for循环有什么问题?

最好的问候

【问题讨论】:

    标签: bash gnuplot


    【解决方案1】:

    您似乎以一种奇怪的方式混合了 bash 和 gnuplot。使用 bash 脚本尝试使用插入的变量动态生成 gnuplot 脚本是一种快速迷惑自己的方法。它也使阅读和调试变得困难。很容易忘记 bash 正在评估什么以及 gnuplot 正在评估什么。

    当我看到你的第一行

    ArrLength=${#arr[@]} #Get the length of array
    

    我可以看到这是 bash 代码,因为 gnuplot 会解释以第一个 # 开头的注释。 (这也是 bash 的数组语法,而不是 gnuplot 的。)美元符号 $ 在 gnuplot 中具有不同的含义。 $ 不是标记变量标识符,而是列号运算符($2 是第 2 列,$i 是列 i 等)。所以看线

    plot for [p=0:$ArrLength] "$outputdir/${arr[$p]}/values.csv" using 1:2 with lines title "${arr[$p]}"
    

    这显然是一行bash语法,显然是在一个字符串里面试图写一行gnuplot。 Bash 将评估变量$ArrLength$outputdir${arr[$p]},并将它们替换为它们的一些值字符串。另请记住,p 是 gnuplot 中的变量,而不是 bash 中的变量。 Bash 会将$p 评估为 something(如果尚未定义,则为空字符串)。您不能期望 gnuplot 变量 p 被用作 ${arr[$p]} 的 bash 评估中的索引,然后以某种方式为 gnuplot 循环的每次迭代生成不同的字符串。

    总之,你写的不是gnuplot语法,也不是一个最小而完整的bash脚本。目前尚不清楚您打算如何将 bash 和 gnuplot 像这样组合在一起,但似乎您将它们结合得太紧了。


    我的建议是编写一个 bash 脚本 并编写一个 gnuplot 脚本(作为单独的文件)。 Gnuplot 有自己的流程控制、迭代循环和变量评估。你可以为你需要它做的所有事情的一般情况编写一个独立的 gnuplot 脚本,然后在你的 bash 脚本的命令行上给出它的细节。

    例如,您的子目录似乎都是 10 的倍数,并且总是从 10 开始。唯一可变的方面是有多少(最后一个是什么)。假设最后一个值以某种方式存储在 gnuplot 变量 last 中。另外,假设我们还以某种方式在outputdir 中拥有基本输出目录:

    (在 gnuplot 脚本中,命名为 plot.gp):

    plot for [p=10:last:10] sprintf("%s/%d/values.csv", outputdir, p) with lines title sprintf("%d", p)
    

    for [p=10:last:10] 表示从 10 迭代到 last(含),每次迭代增加 10。第一个sprintf() 函数(如C)使用outputdirp(两者都是gnuplot 中的变量)构建一个字符串。 using 1:2 不是必需的,因为前两列是使用 with lines 的默认列,但如果您想明确说明,可以包含它们。第二个sprintf() 从迭代变量p 构建一个标题字符串。

    现在,这假设 outputdirlast 具有有意义的值。当您在命令行上调用 gnuplot 时,您可以从 bash 脚本中分配这些值:

    (在 bash 脚本中,调用 gnuplot 脚本)

    gnuplot -e "last=40" -e "outputdir=\"$outputdir\"" plot.gp
    

    -e 选项告诉 gnuplot 在运行文件plot.gp 中的脚本之前评估给定的字符串。在此示例中,gnuplot 变量 last 的值将是 40,而 gnuplot 变量 outputdir 将具有 bash 评估 $outputdir 的任何值。注意双引号内的转义双引号。外面的双引号是为了让 bash 对字符串内的变量求值($outputdir 需要被 bash 求值)。内部(转义)引号用于分隔 gnuplot 代码中的字符串。例如,如果 bash 将 $outputdir 计算为 data,那么 gnuplot 将看到 outputdir="data",这是一个字符串到变量 outputdir 的有效 gnuplot 分配。如果需要,您可以将这两个 -e 选项合二为一:

    gnuplot -e "last=40;outputdir=\"$outputdir\"" plot.gp
    

    您可能希望在 bash 中使用数组中的 last 的值,而不是像这样对其进行硬编码。所以在实践中它可能看起来更像

    gnuplot -e "last=${arr[${#arr[@]}-1]};outputdir=\"$outputdir\"" plot.gp
    

    或者,如果你有 bash 4.3 或更高版本,你应该可以使用negative index

    gnuplot -e "last=${arr[-1]};outputdir=\"$outputdir\"" plot.gp
    

    请注意,数组变量的使用周围没有转义引号。预计它将评估为整数(4090 等),我们希望将 last 分配给整数,而不是像 outputdir 这样的字符串。

    如果这个字符串看起来很复杂,请尝试像这样考虑整个脚本。很容易对 bash 在做什么以及 gnuplot 在做什么感到困惑。

    总而言之,写一个 bash 脚本和一个 单独的 gnuplot 脚本。 Gnuplot 能够处理一般情况。从 bash 开始,只需即时给出一些细节,不要试图动态生成整个脚本。它确实让事情变得更简单。

    【讨论】:

    • 不知道 gnuplot 不会接受 bash 脚本中的 bash 语法。我将不得不重新考虑这一点。感谢您详尽而冗长的解释。
    猜你喜欢
    • 2013-03-25
    • 2012-11-13
    • 2014-10-14
    • 1970-01-01
    • 2021-03-25
    • 2015-05-08
    • 1970-01-01
    • 1970-01-01
    • 2021-04-17
    相关资源
    最近更新 更多