我们如何执行屏幕循环,每个屏幕执行一个循环以便选择内部和外部变量?
考虑一下:
# does not print i
for i in 1 2; do
screen -dmS screen-${i} bash -c 'for j in 1 2; do
echo screen-${i}-iter-${j}
done; exec bash'
done
这个不行,因为非插值引号暗示里面的变量是固有的bash -c
,对吧?所以${i}
是空的。
另请考虑:
# does not print j
for i in 1 2; do
screen -dmS screen-${i} bash -c "for j in 1 2; do
echo screen-${i}-iter-${j}
done; exec bash"
done
第二个方法同样不起作用,因为插入引号暗示了在 之外的插入bash -c
。所以${j}
是空的。
我该如何解决这个问题?用另一种语言来做是否更合适?
我尝试使用 Python,但无法避免当 Python 脚本完成时屏幕会死掉。所以我看不到屏幕上的内容。无论如何,一旦调用它的人死掉,屏幕就必须保持打开状态。为了完整性,我尝试使用 Python。
import subprocess
def screen(i):
proc = subprocess.Popen('screen -S screen-'+str(i), shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE)
command = f"for j in 1 2; do\necho screen-{i}-iter-${{j}}\ndone; exec bash\n"
proc.stdin.write('{}'.format(command).encode('utf-8'))
for i in range(2):
screen(i)
答案1
您可以连接不同引号的字符串(或根本不用引号)。例如,在 中"screen-${i}-"iter'-${j}'
有双引号screen-${i}-
、无引号iter
和单引号-${j}
。这样,您可以告诉 shell 展开${i}
但不展开。如果的${j}
值为,则结果单词将是。$i
1
screen-1-iter-${j}
在您的特定情况下,$i
采用数值并且$IFS
是默认值,只需混合引号即可。
但是,使用bash -c
(或sh -c
或ssh
或任何以 shell 代码为参数的东西)嵌入由外壳扩展的变量,其缺陷如下:嵌入{}
扩展为find
或xargs
,尤其是当变量来自不受信任的来源时。
例如如果你的值为$i
,; rm -f /important/file;
并且你让外壳将其展开并将结果嵌入到即将被解释为外壳代码的字符串中,那么 bash -c
由 生成的screen
将解释包含 的代码echo screen-; rm -f /important/file;
,并且它将尝试删除/important/file
。
使用bash -c
(或sh -c
)正确的一般方法是这样的:
i=whatever
bash -c '
for j in 1 2; do
echo "screen-${1}-iter-${j}"
done
' my-bash "$i"
(my-bash
其中任意字符串)。现在,扩展的值作为额外参数$i
传递给bash
。在 shell 代码中,您可以使用${1}
(或$1
) 检索它。内壳将不会将该值解释为 shell 代码。
总是更喜欢静态 shell 代码;将变量作为参数或在环境中传递。
另请注意正确双引号 $i
对于外壳和screen-${1}-iter-${j}
内壳。在第一个代码片段中,外壳看到的是未加引号的screen-${i}
,而内壳看到的是未加引号的screen-${i}-iter-${j}
。你可以这样做,因为1
和2
是安全值(至少对于默认的$IFS
)。不过,在我看来,总是习惯性地正确引用比每次都思考可能的值是否安全更容易,也更不容易出错。