我有以下 bash 脚本代码,用于在不同的终端选项卡中使用不同的输入参数打开 python 脚本的多个会话。
#!/bin/bash
cd /home/me/experiment
tab=" --tab-with-profile=Default"
options=() #(--tab --title=Terminal)
cmds[1]="./new_path.py -s 593000 -f 593200"
titles[1]="0593000_0593200"
cmds[2]="./new_path.py -s 593200 -f 593400"
titles[2]="0593200_0593400"
cmds[3]="./new_path.py -s 593400 -f 593600"
titles[3]="0593400_0593600"
cmds[4]="./new_path.py -s 593600 -f 593800"
titles[4]="0593600_0593800"
cmds[5]="./new_path.py -s 593800 -f 594000"
titles[5]="0593800_0594000"
for i in 1 2 3 4 5; do
options+=($tab --title="${titles[i]}" -e "bash -c \"${cmds[i]} ; bash\"" )
done
gnome-terminal "${options[@]}"
exit 0
如何避免使用 for 循环和 bash 脚本变量多次输入相同的命令?
答案1
您可以将脚本缩短为:
#!/bin/bash
cd /home/me/experiment
args=()
for ((step=200, s=593000; (f=s+step)<=594000; s+=step)); do
cmd="./new_path.py -s $s -f $f"; printf -v title '%07d_%07d' $s $f
args+=(--tab-with-profile=Default -t $title -e "sh -c \"$cmd; bash\"")
done
gnome-terminal "${args[@]}"
为了消除重复,我完全删除了cmds
和数组,并且titles
将这些字符串的构造移到循环中,以便可以实现自动化。
我当然测试过这个;虽然在我的测试中我使用了mate-terminal
而不是gnome-terminal
。它们非常相似,mate-terminal
都是 的一个分支gnome-terminal
,如果因为这种差异而出现任何问题,我会非常惊讶;mate-terminal
的功能是 提供的功能的子集gnome-terminal
。最重要的是,两者都接受相同的--tab-with-profile
、-t
和-e
选项。
如果您感兴趣的话,下面将更详细地解释上述代码的工作原理以及为什么选择该实现。
我没有循环遍历序列1 2 3 4 5
,因为这对于问题来说毫无意义,而是将s
传递给./new_path.py
after 的值设为循环变量-s
。我还定义f
并确保它在循环的每次迭代中都取一个比 大 200 的值;这就是传递给after 的s
值。./new_path.py
-f
这使得构建每个迭代cmd
和title
字符串变得容易。(是传递给之后的cmd
值,是传递之后的值。)gnome-terminal
-e
title
-t
- 制作
cmd
特别简单。s
并且f
可以不加修改地出现在字符串中,在双引号表达式中取消引用为$s
和。$f
- 对于
title
我注意到你手动编写的字符串中的前导零(例如)。我推断你可能希望在标题数字少于七位时用零填充,并使用 bash 的"0593000_0593200"
printf
内置为了达成这个。
在编写循环时,有几种可能的构造可供选择。除了更常见的语法外,bash 还支持 C 风格的语法,我决定使用它。for variable in list
for
(( initialization; condition; increment-or-other-action))
我决定如何编写循环的目标包括:
- 使得稍后修改循环时,每个数字只需在一个地方更改(例如,避免
200
在代码中多次出现),和 - 反映正在解决的根本问题的逻辑。
在 bash 中,数字序列通常最好用(或仅适用于连续序列)生成。但是{start..end..step}
{start..end}
括号扩展之前执行shell 参数扩展, 所以start
,end
, 和step
不能是变量(如$step
)。
循环(以及循环内的设置)可以解决这个问题,如果我只关心代码的简易维护,我可能会那样做。for s in seq 593000 593800 $step
f=$((s+step))
根据你原来的脚本,似乎正在解决的根本问题是,或者是就像是, 这:
- 执行(某些实验或计算)的运行。
./new_path.py
改变一些重要参数,其起始值和最终值通过-s
和指定-f
。- 重要参数范围从593000到594000。
- 以 200 为块依次变化。通过并行运行五个这样的任务来覆盖范围。
这表明,以这样的方式编写循环很有价值:最低-s
值和最高-f
值被指定(而不是最低值和最高-s
值)。我使用了 C 风格的for ((...))
循环,它以相当简洁和可读的方式实现了这一点。
请注意,这不是唯一的方法,也没有任何一种“正确方法”。特别是,我可以在循环之前定义step
and max
(并初始化s
为其最小值),然后使用带有算术比较的 while 循环(通过((
与<=
,或[[
-le
条件中含有)。
与您的原始脚本相比,我做了一些其他更改,但这些更改并不大,完全是风格偏好的问题。
我将其重命名
options
为,args
因为“选项”通常专门用于表示标志(例如以 和 开头的标志-
)--
,而不是所有命令行参数。由于该
+=
运算符用于构建参数数组,而不是最终的命令字符串本身,因此其元素中不需要前导或尾随空格。所以我删除了 之前的前导空格--tab-with-profile
。在传递给
gnome-terminal
,我将其改为--title
以便-t
与 的选择保持一致-e
。 (-t
是 的缩写形式--title=
;-e
是 的缩写形式--command=
。)这也使它更容易阅读,因为它可以$title
成为一个单独的数组元素,而不必像 那样组合成字符串"--title=$title"
。我使用
sh -c ...
而不是bash -c ...
作为传递给的命令gnome-terminal
,因为通用sh
(Ubuntu 中更简单、更轻量dash
)足以完成用 链接两个简单命令的任务;
。(我没有bash
将after更改;
为sh
,因为完成后在每个选项卡中运行的交互式 shell./new_path.py
当然应该仍然是 bash。)在此背景下,这无疑是不是这是性能方面的考虑,只是风格问题。我不是建议使用
sh -c
而bash -c
不是正确的方式或客观上更可取。