0

I'm able to call diff via exec() just fine with files, like so:

exec('diff -N -u '.escapeshellarg($oldfile).' '.escapeshellarg($newfile), $lines);

However, attempting to do this with arbitrary strings fails:

exec('diff -N -u <(echo '.escapeshellarg($oldstring).') <(echo '.escapeshellarg($newstring).')', $lines);

If I copy the command being run into bash, it works just fine. But when run in PHP I get no output. Even 2>&1 doesn't yield anything. Capturing the status code yields 1, which should indicate that diff found differences, but I still get 1 even in the case where $newstring === $oldstring.

So I'm not quite sure what's going on. I can only assume that, for some reason, exec doesn't like process substitutions? Any ideas?

5
  • exec runs the command with sh, which does not support process substitution. You'd need to run it with bash Commented Jan 21, 2018 at 18:39
  • @thatotherguy Good point, although exec('echo $SHELL'); yields /bin/bash. Commented Jan 21, 2018 at 18:53
  • @thatotherguy Interesting - I tried wrapping the whole thing in '/bin/bash -c '.escapeshellarg('diff ...') and suddenly it's working. That leaves more questions (like why $SHELL is /bin/bash) but at least it works! Commented Jan 21, 2018 at 18:56
  • $SHELL is not the currently running shell. It's the user's login shell, similar to how $EDITOR is the user's editor and $PAGER is their preferred pager. Commented Jan 21, 2018 at 20:16
  • @thatotherguy All righty, fair enough. I got it working thanks to your comment, so feel free to post as answer :) Commented Jan 21, 2018 at 20:27

2 Answers 2

1

PHP's exec runs the command with /bin/sh, which does not support process substitution (even when sh is provided by bash).

You can instead run your command explicitly with bash -c.

Sadly, PHP has no convenience functions for safe and robust execv style execution, so the easiest way to do that to build your diff command and then escape the whole thing:

exec('bash -c ' . escapeshellarg('diff -N -u <(echo '.escapeshellarg($oldstring).') <(echo '.escapeshellarg($newstring).')'), $lines);
Sign up to request clarification or add additional context in comments.

2 Comments

Thank you. As it turns out, it doesn't like longer strings anyway (worked in testing, then tried with an 80Kb string and it died spectacularly XD) so I wound up going with temporary files and back to sh.
Yep yep.. The limit is OS dependent but usually 128k-256k.
0

What shell is being used? Make sure diff is in the $PATH, otherwise command will fail.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.