11

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.

4
  • 2
    Don't you want $string to be passed as its actual value? It currently is passed as string $string value. Use double quotes in shell_exec("./response.sh $string"); so PHP can parse it. As for Bash I have no clue so I won't put it as an answer. Commented Sep 22, 2015 at 15:47
  • I'm getting the following error when I use double quotes: sh: 1: Syntax error: word unexpected (expecting ")") Commented Sep 22, 2015 at 15:54
  • I know you have already selected an answer, but I would wonder if what you are looking for is simply the ability to select options, if maybe you would be more interested in having the PHP script prompt for input. Commented Sep 22, 2015 at 17:47
  • that's exactly what I'm looking for. I initially wanted to make the prompt in BASH b/c I have an idea of how I want to format it and make it look nice but then I realized it would probably be easier to do it all in PHP script. I'll give your solution a try. Commented Sep 22, 2015 at 18:59

4 Answers 4

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

$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
Sign up to request clarification or add additional context in comments.

2 Comments

how would the bash script look? When response.sh is called, it's suppose to display the options on the terminal and prompt the user for an input then pass the input back to the php script.
I'm not going to do your homework; if my answer helps you with your specific question; please accept this answer and complete the script yourself.
3

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;
}

Comments

2

You can have your shell script as this:

#!/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
$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 -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 Comments

Since the user input comes before the output from the bash script is shown, it might be easier if I have to do it all in the php script. thanks again.
This happens because the PHP variable $user_response contains the contents of STDOUT and not the actual value the user responded with.
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.

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]};

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.