如何在使用 Spawn 的 Expect 脚本中调用 Awk 打印变量?
How do I call Awk Print Variable within Expect Script that uses Spawn?
我一直在创建一些基本的系统健康检查,其中一项检查包括 yum 存储库健康状态,它使用名为 'knife' 的 Chef 工具之一。但是,当我尝试 awk 列时,我得到
can't read "4": no such variable.
这是我目前使用的:
read -s -p "enter password: " pass
/usr/bin/expect << EOF
spawn knife ssh -m host1 "sudo bash -c \"yum repolist -v| grep Repo-name| awk '{print }'\" "
expect {
-nocase password: { send "$pass\r" }; expect eof }
}
EOF
我也尝试过其他变体,例如用双花括号替换 awk 单引号,转义变量,甚至将变量设置为命令,并不断得到相同的否定结果:
awk {{print }}
awk '{print $4}'
awk '\{print $4\}'
awk {\{print $4\}}
有谁知道如何在将变量发送到 ssh 主机的 spawned knife ssh 命令中传递这个 awk 列选择器变量?
这一行:
spawn knife ssh -m host1 "sudo bash -c \"yum repolist -v|grep Repo-name|awk '{print }'\""
有多层引用(Tcl/Expect、ssh、bash、awk),而且是不同类型的引用。这样的事情通常非常讨厌,可能需要使用相当多的反斜杠来说服值不受干扰地通过外层。特别是,Tcl 和 shell 都希望获得使用以 $
开头并继续使用 alphanumerics 的变量。 (深入的反斜杠本身需要用更多的反斜杠引用,使代码难以阅读和准确理解。)
spawn knife ssh -m host1 "sudo bash -c \"yum repolist -v|grep Repo-name|awk '{print \$4}'\""
但是,我们有一个 大 优势:我们可以将大部分代码放在外层的大括号中,因为我们实际上并没有在其中替换任何来自 Tcl 的东西。
spawn knife ssh -m host1 {sudo bash -c "yum repolist -v|grep Repo-name|awk '{print $4}'"}
大括号内的东西是常规的 shell 代码,不是 Tcl。事实上,我们可以进一步简化,因为 grep
和 awk
都不需要提升:
spawn knife ssh -m host1 {sudo bash -c "yum repolist -v"|grep Repo-name|awk '{print }'}
根据 sudo
配置,您甚至可以执行此操作(我实际上更希望人们在我控制的系统上这样做,而不是授予对 root shell 的一般访问权限出来):
spawn knife ssh -m host1 {sudo yum repolist -v|grep Repo-name|awk '{print }'}
如果我的 awk
足够好,您可以像这样摆脱 grep
:
spawn knife ssh -m host1 {sudo yum repolist -v|awk '/Repo-name/{print }'}
这看起来更易于管理了。然而,如果你想用 Repo-name
代替 Tcl 变量,你需要做更多的工作来重新引入反斜杠,但现在它比以前更温和了,因为造成麻烦的复杂层更少了。
set repo "Repo-name"
spawn knife ssh -m host1 "sudo yum repolist -v|awk '/$repo/{print $4}'"
实际上,我更有可能完全摆脱 awk
并在 Tcl 代码中完成该部分,以及设置 key-based 直接访问 root 帐户,允许避免 sudo
,但这已经超出了你原来问题的范围。
包含很好的分析和建议。
用 为什么用 \$4
替换 </code> 的解释来补充它</strong>:</p>
<p>最终目的是让<code>awk
看到</code>as-is,所以必须逃避所有<code>$
有特殊意义的中间层[=48] =]
我将在下面的示例中使用简化的命令。
让我们先暂时删除shell层,通过引用开头的here-doc分隔符作为'EOF'
,这使得内容表现得像 single-quoted 字符串;即,shell 将内容视为 文字 ,而不应用扩展:
expect <<'EOF' # EOF is quoted -> literal here-doc
spawn bash -c "awk '{ print $1 }' <<<'hi there'"
expect eof
EOF
输出:
spawn bash -c awk '{ print }' <<<'hi there'
hi
请注意,</code> 必须转义为 <code>$1
以防止 expect
将其解释为 expect
变量参考。
鉴于您问题中的 here-doc 使用 未加引号的 开头 here-doc 定界符 (EOF
),shell将内容视为 double-quoted 字符串;即,应用了 shell 扩展 。
鉴于 shell 现在首先扩展脚本,我们必须为 shell 添加额外的转义层 ,方法是在 [=86] 前面加上=]两个额外的\
到$1
:
expect <<EOF # EOF is unquoted -> shell expansions happen
spawn bash -c "awk '{ print \$1 }' <<<'hi there'"
expect eof
EOF
这会产生与上面相同的输出。
根据解析不带引号的 here-doc 的规则(如前所述,它被解析为 double-quoted 字符串),shell 将 \
转换为将 \
和 $1
合并为文字 </code>,合并为文字 <code>$1
,这是 expect
脚本需要看到的内容。
(在 shell 中用 echo "\$1"
验证。)
使用 command-line 参数和 引用 简化方法 here-doc:
如您所见,多层引用(转义)可能会造成混淆。
避免问题的一种方法是:
使用 引用 here-doc,这样 shell 就不会以任何方式解释它,这样您就可以关注expect
的报价需求
通过command-line参数传递任何shell变量值并引用它们从 expect
脚本内部作为 表达式 (直接或首先将它们分配给 expect
变量)。
expect -f - 'hi there' <<'EOF'
set txt [lindex $argv 0]
spawn bash -c "awk '{ print $1 }' <<<'$txt'"
expect eof
EOF
文本 hi there
作为第一个(也是唯一的)command-line 参数传递,可以在脚本中引用为 [lindex $argv 0]
。
(-f -
只是明确地告诉 expect
从 stdin 读取它的脚本,这是区分脚本和参数所必需的)。
set txt ...
创建 expect
变量 $txt
,然后可以不加引号或作为 double-quoted 字符串的一部分使用。
要在 expect
中创建 文字 字符串,请使用 {...}
(相当于 shell 的 '...'
) .
我一直在创建一些基本的系统健康检查,其中一项检查包括 yum 存储库健康状态,它使用名为 'knife' 的 Chef 工具之一。但是,当我尝试 awk 列时,我得到
can't read "4": no such variable.
这是我目前使用的:
read -s -p "enter password: " pass
/usr/bin/expect << EOF
spawn knife ssh -m host1 "sudo bash -c \"yum repolist -v| grep Repo-name| awk '{print }'\" "
expect {
-nocase password: { send "$pass\r" }; expect eof }
}
EOF
我也尝试过其他变体,例如用双花括号替换 awk 单引号,转义变量,甚至将变量设置为命令,并不断得到相同的否定结果:
awk {{print }}
awk '{print $4}'
awk '\{print $4\}'
awk {\{print $4\}}
有谁知道如何在将变量发送到 ssh 主机的 spawned knife ssh 命令中传递这个 awk 列选择器变量?
这一行:
spawn knife ssh -m host1 "sudo bash -c \"yum repolist -v|grep Repo-name|awk '{print }'\""
有多层引用(Tcl/Expect、ssh、bash、awk),而且是不同类型的引用。这样的事情通常非常讨厌,可能需要使用相当多的反斜杠来说服值不受干扰地通过外层。特别是,Tcl 和 shell 都希望获得使用以 $
开头并继续使用 alphanumerics 的变量。 (深入的反斜杠本身需要用更多的反斜杠引用,使代码难以阅读和准确理解。)
spawn knife ssh -m host1 "sudo bash -c \"yum repolist -v|grep Repo-name|awk '{print \$4}'\""
但是,我们有一个 大 优势:我们可以将大部分代码放在外层的大括号中,因为我们实际上并没有在其中替换任何来自 Tcl 的东西。
spawn knife ssh -m host1 {sudo bash -c "yum repolist -v|grep Repo-name|awk '{print $4}'"}
大括号内的东西是常规的 shell 代码,不是 Tcl。事实上,我们可以进一步简化,因为 grep
和 awk
都不需要提升:
spawn knife ssh -m host1 {sudo bash -c "yum repolist -v"|grep Repo-name|awk '{print }'}
根据 sudo
配置,您甚至可以执行此操作(我实际上更希望人们在我控制的系统上这样做,而不是授予对 root shell 的一般访问权限出来):
spawn knife ssh -m host1 {sudo yum repolist -v|grep Repo-name|awk '{print }'}
如果我的 awk
足够好,您可以像这样摆脱 grep
:
spawn knife ssh -m host1 {sudo yum repolist -v|awk '/Repo-name/{print }'}
这看起来更易于管理了。然而,如果你想用 Repo-name
代替 Tcl 变量,你需要做更多的工作来重新引入反斜杠,但现在它比以前更温和了,因为造成麻烦的复杂层更少了。
set repo "Repo-name"
spawn knife ssh -m host1 "sudo yum repolist -v|awk '/$repo/{print $4}'"
实际上,我更有可能完全摆脱 awk
并在 Tcl 代码中完成该部分,以及设置 key-based 直接访问 root 帐户,允许避免 sudo
,但这已经超出了你原来问题的范围。
用 为什么用 我将在下面的示例中使用简化的命令。 让我们先暂时删除shell层,通过引用开头的here-doc分隔符作为 输出: 请注意, 鉴于您问题中的 here-doc 使用 未加引号的 开头 here-doc 定界符 ( 鉴于 shell 现在首先扩展脚本,我们必须为 shell 添加额外的转义层 ,方法是在 [=86] 前面加上=]两个额外的 这会产生与上面相同的输出。 根据解析不带引号的 here-doc 的规则(如前所述,它被解析为 double-quoted 字符串),shell 将 如您所见,多层引用(转义)可能会造成混淆。 避免问题的一种方法是: 使用 引用 here-doc,这样 shell 就不会以任何方式解释它,这样您就可以关注 通过command-line参数传递任何shell变量值并引用它们从 文本 要在 \$4
替换 </code> 的解释来补充它</strong>:</p>
<p>最终目的是让<code>awk
看到</code>as-is,所以必须逃避所有<code>$
有特殊意义的中间层[=48] =]
'EOF'
,这使得内容表现得像 single-quoted 字符串;即,shell 将内容视为 文字 ,而不应用扩展:expect <<'EOF' # EOF is quoted -> literal here-doc
spawn bash -c "awk '{ print $1 }' <<<'hi there'"
expect eof
EOF
spawn bash -c awk '{ print }' <<<'hi there'
hi
</code> 必须转义为 <code>$1
以防止 expect
将其解释为 expect
变量参考。EOF
),shell将内容视为 double-quoted 字符串;即,应用了 shell 扩展 。\
到$1
:expect <<EOF # EOF is unquoted -> shell expansions happen
spawn bash -c "awk '{ print \$1 }' <<<'hi there'"
expect eof
EOF
\
转换为将 \
和 $1
合并为文字 </code>,合并为文字 <code>$1
,这是 expect
脚本需要看到的内容。
(在 shell 中用 echo "\$1"
验证。)
使用 command-line 参数和 引用 简化方法 here-doc:
expect
的报价需求expect
脚本内部作为 表达式 (直接或首先将它们分配给 expect
变量)。expect -f - 'hi there' <<'EOF'
set txt [lindex $argv 0]
spawn bash -c "awk '{ print $1 }' <<<'$txt'"
expect eof
EOF
hi there
作为第一个(也是唯一的)command-line 参数传递,可以在脚本中引用为 [lindex $argv 0]
。
(-f -
只是明确地告诉 expect
从 stdin 读取它的脚本,这是区分脚本和参数所必需的)。set txt ...
创建 expect
变量 $txt
,然后可以不加引号或作为 double-quoted 字符串的一部分使用。expect
中创建 文字 字符串,请使用 {...}
(相当于 shell 的 '...'
) .