1

The following command executes fine in bash:

Command:

bash -c "$(echo 'H4sIAArQ/mAAA1WMuw7CIBRAd77ihLJqtKuTg19hHIjetiQU0svl/1sn43weaeKJD4PnlI2R1w1bpOBA3kvF340ssX1Z1LmvUqyhsvWk8jl7nOQmP/2x9ZixSlXWqnLcYvlrw4VwJYxHOiW3AwCHgS2AAAAA' | base64 --decode | zcat)" - -a -b

Output:

Equal to or more than 2 arguments - -a -b

Wanted to know - how can I achieve this using Java's ProcessBuilder?

I tried the following:

ProcessBuilder processBuilder = new ProcessBuilder(args);

where args are:

bash
-c
"$(echo 'H4sIAArQ/mAAA1WMuw7CIBRAd77ihLJqtKuTg19hHIjetiQU0svl/1sn43weaeKJD4PnlI2R1w1bpOBA3kvF340ssX1Z1LmvUqyhsvWk8jl7nOQmP/2x9ZixSlXWqnLcYvlrw4VwJYxHOiW3AwCHgS2AAAAA' | base64 --decode | zcat)"
-
-a
-b

But I keep on getting the following error:

-: if: command not found

Process finished with exit code 127

Can someone please point out the issue here?

5
  • 1
    I suspect you're taking code that was built to be evaluated by an outer shell, and then using it in an environment where there's only one shell -- an inner one. Commented Jul 26, 2021 at 16:46
  • See replit.com/@CharlesDuffy2/SmartUniqueAnalysts#Main.java Commented Jul 26, 2021 at 17:01
  • By the way: Your compressed base64-string alone is longer (156 bytes) than the uncompressed program itself (124 bytes). Why not execute the uncompressed program directly to safe some resources and avoid the problems? Commented Jul 26, 2021 at 17:12
  • Socowi, the main problem is on a long script (6000 chars). The compression ratio remains 60% for that case. The above problem is just for representation. Commented Jul 26, 2021 at 17:20
  • Makes sense. Thank you for the info and for posting a minimal example instead of the full script. Commented Jul 26, 2021 at 17:23

1 Answer 1

2

Command substitution results, in bash, don't go through all parsing steps. That means that compound commands like if aren't honored, command separators like ; have no syntactic meaning, etc.

If you want to override that and force an additional parsing pass, you need to use eval. Thus:

args = String[]{
  "bash",
  "-c",
  "eval \"$(echo 'H4sIAArQ/mAAA1WMuw7CIBRAd77ihLJqtKuTg19hHIjetiQU0svl/1sn43weaeKJD4PnlI2R1w1bpOBA3kvF340ssX1Z1LmvUqyhsvWk8jl7nOQmP/2x9ZixSlXWqnLcYvlrw4VwJYxHOiW3AwCHgS2AAAAA' | base64 --decode | zcat)\"",
  "-",
  "-a",
  "-b",
}

Why did this work when you ran it in a shell, instead of from a ProcessBuilder? Because that shell you ran it in would perform the command substitution in "$(...)", and put the results of that substitution in the text it passed to the child shell; so the substitution was already done at parsing time.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for the wonderful explanation! Does it works the same way for the args as well? Let's say instead of "-b" I want to pass "eval \"$(echo 'H4sIALLp/mAAA9NN4gIAmuzKeAMAAAA=' | base64 --decode | zcat)\""?
Only the argument immediately after -c is parsed as code. All others keep their literal values. This is important from a security perspective -- otherwise, one could never write a bash script able to safely handle untrusted data.
Thanks, for the clarification.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.