Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass a PHP array to a BASH script?

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:

  1. pass a PHP array to the BASH script
  2. get user input from STDIN
  3. pass the user input back to the PHP script for further processing

Here's my php script:

<?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:

#!/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.

like image 277
cooldood3490 Avatar asked Sep 22 '15 15:09

cooldood3490


People also ask

How do I pass an array in bash?

If you would use "$1" instead of ("$@") you get the first item from the array, "$2"-second item and etc. Here is example of function "array_echo" which writes all items of array. Note: The "n" option in echo command does't output the trailing newline.

How do I run a PHP file in bash?

Put that at the top of your script, make it executable ( chmod +x myscript. php ), and make a Cron job to execute that script (same way you'd execute a bash script). You can also use php myscript.

What is $@ in bash?

bash [filename] runs the commands saved in a file. $@ refers to all of a shell script's command-line arguments. $1 , $2 , etc., refer to the first command-line argument, the second command-line argument, etc. Place variables in quotes if the values might have spaces in them.


2 Answers

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()):

$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

for each in $*; do
  echo $each
done
like image 57
Sjon Avatar answered Oct 21 '22 06:10

Sjon


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

SHELL script:

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

PHP script:

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

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

Result of 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.

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):

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:

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:

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;
}
like image 3
Mike Avatar answered Oct 21 '22 07:10

Mike