47

My script, let's call it execute.php, needs to start a shell script which is in Scripts subfolder. Script has to be executed so, that its working directory is Scripts. How to accomplish this simple task in PHP?

Directory structure looks like this:

execute.php
Scripts/
    script.sh
Zombo
  • 1
  • 62
  • 391
  • 407
tputkonen
  • 5,579
  • 16
  • 60
  • 88

5 Answers5

71

Either you change to that directory within the exec command (exec("cd Scripts && ./script.sh")) or you change the working directory of the PHP process using chdir().

Zombo
  • 1
  • 62
  • 391
  • 407
soulmerge
  • 73,842
  • 19
  • 118
  • 155
  • I had the same question as @op but I'm running the initial script from the terminal. And the working directory is always wherever my terminal is. Usually my home folder. So if I used this command, it would try to go to `/Users/[user]/Scripts/`. I was surprised none of the values in $_SERVER array were "path to this files parent location". Is the only way to do this, to get the full path to the script and break it up into an array, then rebuild it without the last piece? – l008com Mar 24 '19 at 12:38
  • 2
    @l008com Use can use [`__DIR__`](https://www.php.net/manual/en/language.constants.predefined.php#constant.dir) – soulmerge Mar 26 '19 at 12:58
29

The current working directory is the same as the PHP script's current working directory.

Simply use chdir() to change the working directory before you exec().

mauris
  • 42,982
  • 15
  • 99
  • 131
  • 1
    helped me... i used it for auto deployment via git webhooks on various projects using same php script. thanks. – Jigar Tank Apr 25 '15 at 20:49
12

For greater control over how the child process will be executed, you can use the proc_open() function:

$cmd  = 'Scripts/script.sh';
$cwd  = 'Scripts';

$spec = array(
    // can something more portable be passed here instead of /dev/null?
    0 => array('file', '/dev/null', 'r'),
    1 => array('file', '/dev/null', 'w'),
    2 => array('file', '/dev/null', 'w'),
);

$ph = proc_open($cmd, $spec, $pipes, $cwd);
if ($ph === FALSE) {
    // open error
}

// If we are not passing /dev/null like above, we should close
// our ends of any pipes to signal that we're done. Otherwise
// the call to proc_close below may block indefinitely.
foreach ($pipes as $pipe) {
    @fclose($pipe);
}

// will wait for the process to terminate
$exit_code = proc_close($ph);
if ($exit_code !== 0) {
    // child error
}
Inshallah
  • 4,804
  • 28
  • 24
8

If you really need your working directory to be scripts, try:

exec('cd /path/to/scripts; ./script.sh');

Otherwise,

exec('/path/to/scripts/script.sh'); 

should suffice.

timdev
  • 61,857
  • 6
  • 82
  • 92
7

This is NOT the best way :

exec('cd /patto/scripts; ./script.sh');

Passing this to the exec function will always execute ./scripts.sh, which could lead to the script not being executed with the right working directory if the cd command fails.

Do this instead :

exec('cd /patto/scripts && ./script.sh');

&& is the AND logical operator. With this operator the script will only be executed if the cd command is successful.

This is a trick that uses the way shells optimize expression evaluation : since this is an AND operation, if the left part does not evaluate to TRUE then there is not way the whole expression can evaluate to TRUE, so the shells won't event process the right part of the expression.

JulienCC
  • 446
  • 2
  • 11
Ben
  • 191
  • 2
  • 4
  • 6
    I'm having trouble understanding what this answer is saying. Which way is "not the best way"? Are you saying you recommend using && or ; (semi-colon)? Solution you give uses semi-colon, but then in the next sentence you seem to be saying it's better to use &&. – orrd Jul 04 '15 at 23:41
  • A little late to reply but he is saying that && makes sure that the second part (./script.sh) is executed only when the first part (cd /patto/scripts) succeeds. In case of semicolon the second part would be executed whether or not the cd command succeeded. – vijayant Jan 10 '21 at 11:21