在编写代码行时,我们有时需要考虑转义某些字符。
我遇到了一个我无法独自回答的情况
在 PHP 中,exec 命令需要一个用引号 (') 括起来的字符串。我们想要调用命令 /bin/bash -c ,它还需要一个用引号 (') 括起来的字符串
该问题的解决方法如下:
$line = exec('/bin/bash -c \'read -e -p "Check to confirm string: " -i "'.$preFill.'" input; echo $input\'');
只要您不想在向用户建议的文本中插入字符 ('),就没有问题。我们可以看到一些使用 php -a 进行交互模式的示例:
php > $preFill = 'I m reach';
php > $line = exec('/bin/bash -c \'read -e -p "Check to confirm string: " -i "'.$preFill.'" input; echo $input\'');
Check to confirm string: I m reach
php >
问题是:我必须考虑哪些转义规则才能以正确的方式将“我已到达”放入 $preFill 中?
答案1
您需要对传递到的代码bash
和传递到的代码sh
(由php
's开始exec()
)进行一些引用:
$preFill = 'I m reach';
$prompt = 'Check to confirm string: ';
$bash_code = 'IFS= read -re -p ' .
escapeshellarg($prompt) .
' -i ' .
escapeshellarg($preFill) .
' input; printf "%s\n" "$input"';
$sh_code = 'exec /bin/bash -c ' . escapeshellarg($bash_code);
$output = exec($sh_code);
echo $output;
你也忘记了的IFS=
和-r
为read
, 和关于 shell 参数扩展的一些引用,并记住echo
不能用于任意数据。
启动两者sh
并bash
提示用户似乎有点矫枉过正。
答案2
exec('/bin/bash -c \'read -e -p "... " -i "'.$preFill.'" input; echo $input\'');
这里有一些问题。首先,您正在运行一个 shell(通过exec
)来运行bash -c ...
,这意味着 Bash 最终运行的代码也必须为中间 shell 引用。您可以通过使用允许直接运行的函数来避免这种情况bash
,参数位于单独的数组元素(或类似元素)中,而不涉及中间 shell。可能是这样的proc_open()
。
其次,您将数据(该$preFill
变量)嵌入到传递给 Bash 的代码中。通过正确的引用和变量的良好值,这可以工作,但如果$preFill
可以例如包含引号本身,它就会变得痛苦。如果变量可以由用户控制,那么这种痛苦就会变成安全漏洞。更好的方法是将所需的值作为命令行参数传递给 shell,或者作为环境变量,然后在 shell 程序中引用$1
或来获取该值。$val
好吧,运行一个(或两个)shell 似乎read
非常愚蠢。花一些时间寻找直接从 PHP 执行此操作的更好方法可能会更好。
答案3
鉴于您的问题 Y,当使用 PHP 调用系统 shell 来利用 bash 的读取/读取行功能时,如何正确转义字符串,您已经收到了一些关于如何执行此操作的出色答案。
这篇文章试图解决您的问题 X——您最初的问题——我认为这是,如何在 PHP 中捕获用户输入,同时提供预先填充的默认值?通过使用本机 PHP 解决方案,可以避免粗糙的多级字符转义的整个问题,正如 @roaima 在您的帖子的评论中暗示的那样。
readline 的 PHP 文档包括用户 taneli 的示例大约 2009 年,它展示了如何执行您正在寻求的操作,而不必求助于外部 shell 调用。我稍微扩展了这个例子,但我在这里重复 taneli 的工作:
$ cat test.php
<?php
function readline_callback($ret)
{
global $prompt_answer, $prompt_finished;
$prompt_answer = $ret;
$prompt_finished = TRUE;
readline_callback_handler_remove();
}
$prompt_string = 'Check to confirm string: ';
readline_callback_handler_install($prompt_string,
'readline_callback');
$preFill = 'foobar';
for ($i = 0; $i < strlen($preFill); $i++)
{
readline_info('pending_input', substr($preFill, $i, 1));
readline_callback_read_char();
}
$prompt_finished = FALSE;
$prompt_answer = FALSE;
while (!$prompt_finished)
readline_callback_read_char();
echo 'You wrote: ' . $prompt_answer . "\n";
$prompt_string = 'Check to confirm another string: ';
readline_callback_handler_install($prompt_string,
'readline_callback');
$preFill = $prompt_answer;
for ($i = 0; $i < strlen($preFill); $i++)
{
readline_info('pending_input', substr($preFill, $i, 1));
readline_callback_read_char();
}
$prompt_finished = FALSE;
$prompt_answer = FALSE;
while (!$prompt_finished)
readline_callback_read_char();
echo 'You wrote: ' . $prompt_answer . "\n";
?>
输出:
$ php test.php
Check to confirm string: foobar
我将附加“ is the default
”并按Enter。
You wrote: foobar is the default
Check to confirm another string: foobar is the default
我将按下Home并放在Now "
字符串的开头。然后我将按下End并放在" is it's own default.
字符串的末尾,然后按下Enter
You wrote: Now "foobar is the default" is it's own default.