如何在expect -c "send..."命令中正确使用单引号
解决Expect中send命令使用单引号包裹内容的问题
我来帮你理清这个问题,并给出适配PHP escapeshellarg 的解决方案:
问题根源
你遇到的报错本质是Expect的字符串解析规则和shell的引号规则冲突:
- 当你执行
expect -c "send 'one two three'"时,shell会先去掉外层双引号,把send 'one two three'传给Expect。 - 在Expect的语法里,单引号并不是字符串的边界符,只是普通字符。所以Expect会把
'one当作send的第一个参数,two第二个,three'第三个——这明显超出了send命令的参数数量,自然抛出usage: send [args] string错误。
而PHP的 escapeshellarg 只能生成单引号包裹的字符串,这就需要我们调整Expect命令的写法,让它能正确识别这个单引号包裹的内容。
适配PHP代码的解决方案
最优方案是利用Expect的大括号字符串语法:Expect中用 {} 包裹的内容会被当作完整字符串(包括空格、特殊字符),不需要额外转义(除了大括号本身)。我们只需要对 escapeshellarg 生成的结果做简单处理即可:
修改后的PHP代码
$password = escapeshellarg($password); // 第一步:去掉escapeshellarg自动添加的首尾单引号 $password_clean = substr($password, 1, -1); // 第二步:转义密码中的大括号(避免Expect解析时括号不匹配) $password_clean = str_replace(['{', '}'], ['\{', '\}'], $password_clean); // 第三步:用Expect的大括号包裹处理后的密码 $password_expect = '{' . $password_clean . '}'; // 组装最终的Expect命令 $cmd = <<<CMD expect -c 'spawn /var/www/bin/helium-wallet create basic; expect "Password"; send $password_expect; send "\\n"; expect "Confirm"; send $password_expect; send "\\n"; expect eof' CMD;
为什么这个方案可行?
- 假设你的密码是
my pass'word{123}:escapeshellarg会生成'my pass'\''word{123}'- 去掉首尾单引号后得到
my pass'\''word{123} - 转义大括号后变成
my pass'\''word\{123\} - 用大括号包裹后是
{my pass'\''word\{123\}
- Expect执行
send {my pass'\''word\{123\}时,会把大括号内的所有内容当作完整的密码发送,完全符合你的需求。
其他可选方案(不推荐)
如果你坚持想用双引号包裹Expect字符串,需要额外处理密码中的双引号转义,但这种方式更复杂:
$password = escapeshellarg($password); $password_clean = substr($password, 1, -1); // 转义密码中的双引号,让Expect能正确解析 $password_clean = str_replace('"', '\"', $password_clean); $password_expect = "\"$password_clean\""; $cmd = <<<CMD expect -c 'spawn /var/www/bin/helium-wallet create basic; expect "Password"; send $password_expect; send "\\n"; expect "Confirm"; send $password_expect; send "\\n"; expect eof' CMD;
但这种方式需要处理更多特殊字符,不如大括号方案简洁可靠。
内容的提问来源于stack exchange,提问作者Rafał G.
相关产品推荐
相关产品推荐

