如何将PHP数组传递给BASH脚本?

时间:2022-06-12 16:48:47

I have a php script and a bash script. They're in the same directory. I'm running the php script from the command line which will pass an array to the bash script. I'm trying to do the following:

我有一个php脚本和一个bash脚本。它们在同一个目录下。我将从命令行运行php脚本,该脚本将向bash脚本传递一个数组。我想做的是:

  1. pass a PHP array to the BASH script
  2. 将PHP数组传递给BASH脚本
  3. get user input from STDIN
  4. 从STDIN获取用户输入
  5. pass the user input back to the PHP script for further processing
  6. 将用户输入传递回PHP脚本进行进一步处理。

Here's my php script:

这是我的php脚本:

<?php
$a=array("red","green","blue","yellow");

$string = '(' . implode(' ', $a) . ')';  // (red green blue yellow)

$user_response = shell_exec('./response.sh $string');

// do something with $user_response

?>

The BASH script is supposed to read the array from STDIN and prompt the user to select an option:

BASH脚本应该从STDIN读取数组并提示用户选择一个选项:

#!/bin/bash
options=$($1);   # (red green blue yellow) but this isn't working
i=0;
echo "select an option";
for each in "${options[@]}"
do
echo "[$i] $each"
i=$((i+1))
done

echo;

read input;
echo "You picked option ${options[$input]}";
# here's where I want to pass or export the input back to the 
# php script for further processing

When I run the php script it doesn't display the array options.

当我运行php脚本时,它不会显示数组选项。

4 个解决方案

#1


2  

You can have your shell script as this:

您可以将shell脚本如下所示:

#!/bin/bash
options=("$@")

i=0
echo "select an option"
for str in "${options[@]}"; do
   echo "[$i] $str"
   ((i++))
done    
echo    
read -p 'Enter an option: ' input
echo "You picked option ${options[$input]}"

Then have your PHP code as this:

然后让你的PHP代码如下:

<?php
$a=array("red","green","blue","yellow");    
$string = implode(' ', $a);    
$user_response = shell_exec("./response.sh $string");

echo "$user_response\n";
?>

However keep in mind output will be like this when running from PHP:

但是请记住,从PHP运行时输出将是这样的:

php -f test.php
Enter an option: 2
select an option
[0] red
[1] green
[2] blue
[3] yellow

You picked option blue

i.e. user input will come before the output from script is shown.

也就是说,在显示脚本输出之前,用户输入就会出现。

#2


3  

I'd say the easiest would be not to try and emulate an internal bash array, but use 'normal' logic / post-processing. For example; if you simply pass implode(' ', $a) to the bash script (you should also pass it through escapeshellarg()):

我认为最简单的方法不是尝试和模拟一个内部bash数组,而是使用“普通”逻辑/后处理。例如;如果您只是将内爆(',$a)传递给bash脚本(还应该通过escapeshellarg()传递它):

$a=array("red","green","blue","yellow");
$args = implode(' ', array_map('escapeshellarg', $a)); 
$user_response = shell_exec('./response.sh '. $args);

Then you can traverse the arguments in bash using

然后可以使用bash中的参数

for each in $*; do
  echo $each
done

#3


3  

The issue with your solution is that the output of the Shell Script is actually IN the PHP $response variable:

您的解决方案的问题是,Shell脚本的输出实际上是在PHP $response变量中:

SHELL script:

SHELL脚本:

#!/bin/bash
echo "Before prompt"
read -p 'Enter a value: ' input
echo "You entered $input"

PHP script:

PHP脚本:

<?php
$shell = shell_exec("./t.sh");

echo "SHELL RESPONSE\n$shell\n";

Result of php t.php:

由于php t.php:

$ php t.php
Enter a value: foo
SHELL RESPONSE
Before prompt
You entered foo

You captured the entire STDOUT of the Shell Script.

您捕获了Shell脚本的整个STDOUT。

If you are looking to simply pass values to a shell script, the option of $option_string = implode(' ', $array_of_values); will work to place options individually for the script. If you would like something a little more advanced (setting flags, assigning things, etc) try this (https://ideone.com/oetqaY):

如果您只想将值传递给shell脚本,那么可以选择$option_string =内爆(',$array_of_values);将为脚本单独放置选项。如果你想要更高级的东西(设置标志、分配东西等等),试试这个(https://ideone.com/oetqaY):

function build_shell_args(Array $options = array(), $equals="=") {

    static $ok_chars = '/^[-0-9a-z_:\/\.]+$/i';

    $args = array();

    foreach ($options as $key => $val) if (!is_null($val) && $val !== FALSE) {

        $arg     = '';
        $key_len = 0;

        if(is_string($key) && ($key_len = strlen($key)) > 0) {

            if(!preg_match($ok_chars, $key))
                $key = escapeshellarg($key);

            $arg .= '-'.(($key_len > 1) ? '-' : '').$key;
        }

        if($val !== TRUE) {

            if((string) $val !== (string) (int) $val) {
                $val = print_r($val, TRUE);

                if(!preg_match($ok_chars, $val))
                    $val = escapeshellarg($val);

            }

            if($key_len != 0)
                $arg .= $equals;

            $arg .= $val;

        }

        if(!empty($arg))
            $args[] = $arg;

    }

    return implode(' ', $args);
}

That will be about your most comprehensive solution for passing to the command line.

这将是传递到命令行的最全面的解决方案。

If you are instead looking for a way to prompt the user (in general), I would consider staying inside PHP. The most basic way is:

如果您正在寻找一种提示用户(通常)的方法,我将考虑继续使用PHP。最基本的方法是:

print_r("$question : ");
$fp = fopen('php://stdin', 'r');
$response = fgets($fp, 1024); 

Or, to support validating the question, multi-line, and only calling on CLI:

或者,支持验证问题,多行,只调用CLI:

function prompt($message = NULL, $validator = NULL, $terminator = NULL, $include_terminating_line = FALSE) {

    if(PHP_SAPI != 'cli') {
        throw new \Exception('Can not Prompt.  Not Interactive.');
    }

    $return = '';

    // Defaults to 0 or more of any character.
    $validator = !is_null($validator) ? $validator : '/^.*$/';
    // Defaults to a lonely new-line character.
    $terminator = !is_null($terminator) ? $terminator : "/^\\n$/";

    if(@preg_match($validator, NULL) === FALSE) {
        throw new Exception("Prompt Validator Regex INVALID. - '$validator'");
    }

    if(@preg_match($terminator, NULL) === FALSE) {
        throw new Exception("Prompt Terminator Regex INVALID. - '$terminator'");
    }

    $fp = fopen('php://stdin', 'r');

    $message = print_r($message,true);

    while (TRUE) {
        print_r("$message : ");

        while (TRUE) {
            $line = fgets($fp, 1024); // read the special file to get the user input from keyboard

            $terminate = preg_match($terminator, $line);
            $valid = preg_match($validator, $line);

            if (!empty($valid) && (empty($terminate) || $include_terminating_line)) {
                $return .= $line;
            }

            if (!empty($terminate)) {
                break 2;
            }

            if(empty($valid)) {
                print_r("\nInput Invalid!\n");
                break;
            }
        }
    }

    return $return;
}

#4


2  

Since parentheses run what's in them in a sub-shell, which isn't what I think you want...

因为圆括号在子层中运行,这不是我认为你想要的。

I would change this...

我会改变这一切……

$string = '(' . implode(' ', $a) . ')';

To this...

这……

$string = '"' . implode (' ', $a) . '"';

Also, use double quotes here...

另外,在这里使用双引号……

$user_response = shell_exec ("./response.sh $string");

Or break out...

或者打破…

$user_response = shell_exec ('./response.sh ' . $string);

I would therefore also change the BASH to simply accept a single argument, a string, and split that argument into an array to get our options.

因此,我还将BASH更改为只接受一个参数、一个字符串,并将该参数分割为一个数组,以获得我们的选项。

Like so...

像这样…

#!/bin/bash

IFS=' ';
read -ra options <<< "$1";
i=0;

echo "select an option";

for each in "${options[@]}"; do
    echo "[$i] $each";
    i=$((i+1));
done;

unset i;
echo;

read input;
echo "You picked option " ${options[$input]};

#1


2  

You can have your shell script as this:

您可以将shell脚本如下所示:

#!/bin/bash
options=("$@")

i=0
echo "select an option"
for str in "${options[@]}"; do
   echo "[$i] $str"
   ((i++))
done    
echo    
read -p 'Enter an option: ' input
echo "You picked option ${options[$input]}"

Then have your PHP code as this:

然后让你的PHP代码如下:

<?php
$a=array("red","green","blue","yellow");    
$string = implode(' ', $a);    
$user_response = shell_exec("./response.sh $string");

echo "$user_response\n";
?>

However keep in mind output will be like this when running from PHP:

但是请记住,从PHP运行时输出将是这样的:

php -f test.php
Enter an option: 2
select an option
[0] red
[1] green
[2] blue
[3] yellow

You picked option blue

i.e. user input will come before the output from script is shown.

也就是说,在显示脚本输出之前,用户输入就会出现。

#2


3  

I'd say the easiest would be not to try and emulate an internal bash array, but use 'normal' logic / post-processing. For example; if you simply pass implode(' ', $a) to the bash script (you should also pass it through escapeshellarg()):

我认为最简单的方法不是尝试和模拟一个内部bash数组,而是使用“普通”逻辑/后处理。例如;如果您只是将内爆(',$a)传递给bash脚本(还应该通过escapeshellarg()传递它):

$a=array("red","green","blue","yellow");
$args = implode(' ', array_map('escapeshellarg', $a)); 
$user_response = shell_exec('./response.sh '. $args);

Then you can traverse the arguments in bash using

然后可以使用bash中的参数

for each in $*; do
  echo $each
done

#3


3  

The issue with your solution is that the output of the Shell Script is actually IN the PHP $response variable:

您的解决方案的问题是,Shell脚本的输出实际上是在PHP $response变量中:

SHELL script:

SHELL脚本:

#!/bin/bash
echo "Before prompt"
read -p 'Enter a value: ' input
echo "You entered $input"

PHP script:

PHP脚本:

<?php
$shell = shell_exec("./t.sh");

echo "SHELL RESPONSE\n$shell\n";

Result of php t.php:

由于php t.php:

$ php t.php
Enter a value: foo
SHELL RESPONSE
Before prompt
You entered foo

You captured the entire STDOUT of the Shell Script.

您捕获了Shell脚本的整个STDOUT。

If you are looking to simply pass values to a shell script, the option of $option_string = implode(' ', $array_of_values); will work to place options individually for the script. If you would like something a little more advanced (setting flags, assigning things, etc) try this (https://ideone.com/oetqaY):

如果您只想将值传递给shell脚本,那么可以选择$option_string =内爆(',$array_of_values);将为脚本单独放置选项。如果你想要更高级的东西(设置标志、分配东西等等),试试这个(https://ideone.com/oetqaY):

function build_shell_args(Array $options = array(), $equals="=") {

    static $ok_chars = '/^[-0-9a-z_:\/\.]+$/i';

    $args = array();

    foreach ($options as $key => $val) if (!is_null($val) && $val !== FALSE) {

        $arg     = '';
        $key_len = 0;

        if(is_string($key) && ($key_len = strlen($key)) > 0) {

            if(!preg_match($ok_chars, $key))
                $key = escapeshellarg($key);

            $arg .= '-'.(($key_len > 1) ? '-' : '').$key;
        }

        if($val !== TRUE) {

            if((string) $val !== (string) (int) $val) {
                $val = print_r($val, TRUE);

                if(!preg_match($ok_chars, $val))
                    $val = escapeshellarg($val);

            }

            if($key_len != 0)
                $arg .= $equals;

            $arg .= $val;

        }

        if(!empty($arg))
            $args[] = $arg;

    }

    return implode(' ', $args);
}

That will be about your most comprehensive solution for passing to the command line.

这将是传递到命令行的最全面的解决方案。

If you are instead looking for a way to prompt the user (in general), I would consider staying inside PHP. The most basic way is:

如果您正在寻找一种提示用户(通常)的方法,我将考虑继续使用PHP。最基本的方法是:

print_r("$question : ");
$fp = fopen('php://stdin', 'r');
$response = fgets($fp, 1024); 

Or, to support validating the question, multi-line, and only calling on CLI:

或者,支持验证问题,多行,只调用CLI:

function prompt($message = NULL, $validator = NULL, $terminator = NULL, $include_terminating_line = FALSE) {

    if(PHP_SAPI != 'cli') {
        throw new \Exception('Can not Prompt.  Not Interactive.');
    }

    $return = '';

    // Defaults to 0 or more of any character.
    $validator = !is_null($validator) ? $validator : '/^.*$/';
    // Defaults to a lonely new-line character.
    $terminator = !is_null($terminator) ? $terminator : "/^\\n$/";

    if(@preg_match($validator, NULL) === FALSE) {
        throw new Exception("Prompt Validator Regex INVALID. - '$validator'");
    }

    if(@preg_match($terminator, NULL) === FALSE) {
        throw new Exception("Prompt Terminator Regex INVALID. - '$terminator'");
    }

    $fp = fopen('php://stdin', 'r');

    $message = print_r($message,true);

    while (TRUE) {
        print_r("$message : ");

        while (TRUE) {
            $line = fgets($fp, 1024); // read the special file to get the user input from keyboard

            $terminate = preg_match($terminator, $line);
            $valid = preg_match($validator, $line);

            if (!empty($valid) && (empty($terminate) || $include_terminating_line)) {
                $return .= $line;
            }

            if (!empty($terminate)) {
                break 2;
            }

            if(empty($valid)) {
                print_r("\nInput Invalid!\n");
                break;
            }
        }
    }

    return $return;
}

#4


2  

Since parentheses run what's in them in a sub-shell, which isn't what I think you want...

因为圆括号在子层中运行,这不是我认为你想要的。

I would change this...

我会改变这一切……

$string = '(' . implode(' ', $a) . ')';

To this...

这……

$string = '"' . implode (' ', $a) . '"';

Also, use double quotes here...

另外,在这里使用双引号……

$user_response = shell_exec ("./response.sh $string");

Or break out...

或者打破…

$user_response = shell_exec ('./response.sh ' . $string);

I would therefore also change the BASH to simply accept a single argument, a string, and split that argument into an array to get our options.

因此,我还将BASH更改为只接受一个参数、一个字符串,并将该参数分割为一个数组,以获得我们的选项。

Like so...

像这样…

#!/bin/bash

IFS=' ';
read -ra options <<< "$1";
i=0;

echo "select an option";

for each in "${options[@]}"; do
    echo "[$i] $each";
    i=$((i+1));
done;

unset i;
echo;

read input;
echo "You picked option " ${options[$input]};