2

This is my workflow:

ubuntu$ cat test.sh
#!/bin/bash

function getNumber
{
    echo 1
}


ubuntu$ cat test.php
<?php

echo shell_exec("getNumber");



ubuntu$ source test.sh 
ubuntu$ getNumber 
1

ubuntu$ php test.php 
sh: 1: getNumber: not found

Is it possible to change a php script (or may be its call) the way, so it will print "1"? I am looking for a way to call a bash function from php.

I've tried some solutions from here: Is it possible to source a *.sh file using PHP CLI and access exported Env vars in the PHP script?

One of them was: ./test.sh && php -f test.php - but no luck.

Community
  • 1
  • 1
Axalix
  • 2,831
  • 1
  • 20
  • 37

4 Answers4

2

Here's a function that does what you want. It loads spawns a bash process that sources the bash script, then runs the function.

It uses proc-open ( http://php.net/manual/en/function.proc-open.php )

<?php


$value = runBashFunction("test.sh", "getNumber");
var_dump($value);



function runBashFunction($bashfile, $bashfunction) {
    $descriptorspec = array(
         0 => array("pipe", "r"),    // stdin is a pipe that the child will read from
         1 => array("pipe", "w"),    // stdout is a pipe that the child will write to
         2 => array("pipe", "w") // stderr is a pipe too
    );

    $cwd = '.';
    $env = array();

    $process = proc_open('bash', $descriptorspec, $pipes, $cwd, $env);

    if (is_resource($process)) {
            fwrite($pipes[0], "source $bashfile\n");
            fwrite($pipes[0], "$bashfunction\n");
            fclose($pipes[0]);

            $output = stream_get_contents($pipes[1]);
            $error = stream_get_contents($pipes[2]);
            fclose($pipes[1]);
            fclose($pipes[2]);

            // It is important that you close any pipes before calling
            // proc_close in order to avoid a deadlock
            //

            $return_value = proc_close($process);
            //note, $error and $return_value are discarded
            return $output;
    }
    return null;
}
?>
Tim Smith
  • 1,714
  • 2
  • 12
  • 14
  • It always prints int(1) even if I rename in your code `test.sh` to unexisting `test-whatever.sh` – Axalix Nov 02 '15 at 00:59
  • Oops, I was returning the return_value instead of the output. I have edited the script, please try again. – Tim Smith Nov 02 '15 at 01:03
  • Thank you, @Tim. it looks like it works. Let me try it with some parameters. – Axalix Nov 02 '15 at 01:12
  • 1
    It works with parameters and w/o them. It's a bit lenghty, but I don't have any other better working solution now. Thank you very much! – Axalix Nov 02 '15 at 01:33
1

In this particular example, you can combine the source command and the function itself into the same PHP exec call:

var_dump(
    shell_exec('source test.sh && getNumber')
); // string(2) "1
"

Trim the result if you need to to remove the carriage return.

scrowler
  • 24,273
  • 9
  • 60
  • 92
  • This is what it returns... `sh: 1: source: not found NULL` – Axalix Nov 02 '15 at 00:38
  • Which OS are you running? – scrowler Nov 02 '15 at 00:38
  • Linux 3.13.0-37-generic x86_64. Linux Mint 17.1 Rebecca – Axalix Nov 02 '15 at 00:40
  • Yet `source test.sh && getNumber` works from your console? – scrowler Nov 02 '15 at 00:41
  • That's correct and `source test.sh && getNumber` prints "1" as it should. – Axalix Nov 02 '15 at 00:43
  • And you're running this script from the same directory as test.sh? You should try a couple of things - firstly try the full path to `test.sh` and secondly give it execute permissions for your web user and try again – scrowler Nov 02 '15 at 00:44
  • My bad: I checked distro version on a wrong machine. Correct info is: 3.13.0-48-generic and Ubuntu 14.04.3 LTS – Axalix Nov 02 '15 at 00:46
  • I can reproduce a similar error by putting the wrong file path in, although the message is different for me (OSX) - try an absolute file path – scrowler Nov 02 '15 at 00:47
  • Well, you can see my workflow - I didn't change directories - those commands were called one after another. – Axalix Nov 02 '15 at 00:48
  • Agreed, appears to be a file access issue though - try either an absolute path to `test.sh` and/or give it execute permissions for the user – scrowler Nov 02 '15 at 00:49
  • I get the same error as Axalix. I think this is because "sh" doesn't have a "source" command. For me, shell_exec runs sh instead of bash. "source" is a bash builtin command, not an executable. – Tim Smith Nov 02 '15 at 05:18
  • 2
    Cheers @TimSmith - maybe adding bash to the command might alleviate – scrowler Nov 02 '15 at 05:28
  • @RobbieAverill - adding `bash` to the command actually helped! – Axalix Nov 02 '15 at 15:18
1

Another approach is to pass the name of the function you would like to execute into the bash script.

test.sh is the bash script with the function you would like to execute:

#!/bin/bash

function getNumber
{
    echo "fnGetNumber"
}

# Test if the first parameter names a function
if [ "$(type -t $1)" = function ]; then
    $1
else
    echo "$1 is an invalid function"
fi;

test.php is the PHP script that will call the function:

#!/usr/bin/php
<?php
echo `./test.sh getNumber`;

Note that this requires you pass the name of the script as well as the function name.

Credit for function test to: https://stackoverflow.com/a/85903/2182349

If you would like to pass a parameter to the function, you may use:

Updated test.sh:

#!/bin/bash

function getNumber
{
    echo "fnGetNumber $1"
}

if [ "$(type -t $1)" = function ]; then
    $1 "$2"
else
    echo "$1 is an invalid function"
fi;

Updated test.php

<?php
$parm = 'toast';
echo `./test.sh getNumber $parm`;
Community
  • 1
  • 1
user2182349
  • 9,569
  • 3
  • 29
  • 41
  • it does work and technically my question is answered - there is one "although": is it possible to change your code so I can pass parameters to `getNumber`? – Axalix Nov 02 '15 at 01:20
  • There may be multiple (random number) of parameters :) – Axalix Nov 02 '15 at 01:25
  • Enclose the random number of parameters in quotes in PHP and pass them as one, then break them apart in the bash script. – user2182349 Nov 02 '15 at 01:27
  • Thank you. I don't think it is always possible to break them apart, especially when some commands look this way `rsync -aL --rsh="ssh -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null"` Also I can't touch the bash script (it was in my question that I can only touch php and its call). The shell script is a library (list of functions) that I was given and I can't change it. php code is mine - I am free to make any changes I want. – Axalix Nov 02 '15 at 01:32
0

I found an alternative solution. The problem was in Ubuntu and its dash

Running sudo dpkg-reconfigure dash and answering no changed dash to bash, what made source command available from php.

WAS: ls -la /bin/sh => /bin/sh -> dash

NOW: ls -la /bin/sh => /bin/sh -> bash

With enabled source, this worked fine: echo shell_exec('source /full/path/to/file/test.sh && getNumber abc def');

What helped me: https://stackoverflow.com/a/13702876/4621324

One Note: during the change Ubuntu has warned me: Using dash as the system shell will improve the system's overall performance. It does not alter the shell presented to interactive


As I don't really want to change dash to bash on the global level, I decided to go with this solution:

echo shell_exec('/bin/bash -c "source /full/path/to/file/test.sh && getNumber abc def"');

Community
  • 1
  • 1
Axalix
  • 2,831
  • 1
  • 20
  • 37