This works of course. I can run ./test1.sh cats and the argument cats is passed correctly to ls.

$ cat test1.sh
ls -l $1

Can you tell me how do I make this second example — the same content as the first one, but encrypted — work the same way? The password is "password". openssl outputs the above script, and ls -l runs, but how do I pass the command line argument to it?

$ cat test2.sh
echo "U2FsdGVkX1/m01Eg0CvZ7DiBi5Nf81+JrCWfKIDI9WtbkTZIVLhZskkKIioVfbSe"|openssl enc -base64 -d -aes-256-cbc|sh $1

To clarify, test2.sh is equivalent to

$ cat test3.sh
printf '%s\n' '#!/bin/sh' 'ls -l $1' | sh $1
  • What do you want the script to do with the argument? – Scott Nov 21 '18 at 4:22
  • Same as in test1.sh, pass it to ls -l. – Jack Brown Nov 21 '18 at 5:36
  • 2
    There's no ls -l in test2.sh – muru Nov 21 '18 at 6:18
  • 1
    @muru There is, inside the encrypted script in test2.sh. – Kusalananda Nov 21 '18 at 7:35

This script will ask for a password, decrypt the encrypted script and run it with the first command line option passed to the main script:


bash <(
    openssl enc -base64 -d -aes-256-cbc <<<'U2FsdGVkX1/m01Eg0CvZ7DiBi5Nf81+JrCWfKIDI9WtbkTZIVLhZskkKIioVfbSe'
) "$1"

This does not use the #!-line of the decrypted script but assumes it will run okay under bash (and that it is always decrypted successfully). The decrypt script is passed to a bash interpreter by means of a process substitution on the openssl invocation, along with the first command line argument.

To write the decrypted script to disk, run it only if it was decrypted properly (using its intended interpreter), and then delete it:


trap 'rm -f "$tmpscript"' EXIT

if ! openssl enc -base64 -d -aes-256-cbc <<<'U2FsdGVkX1/m01Eg0CvZ7DiBi5Nf81+JrCWfKIDI9WtbkTZIVLhZskkKIioVfbSe' >$tmpscript
    exit 1

chmod +x "$tmpscript"
"$tmpscript" "$1"

Would you want to pass all the command line parameters of the main script to the decrypted script, you should change "$1" in the above examples to "$@". The double quotes are important as they ensure that the individual arguments are passed without getting split on whitespaces and without undergoing filename globbing.

  • Thanks, the 1st works also with my mysql script where command line arguments are in the middle. – Jack Brown Nov 21 '18 at 7:12

You had it in an earlier version of the question — pass a -s option to sh:

echo "U2FsdGVkX1/m01Eg0CvZ7DiBi5Nf81+JrCWfKIDI9WtbkTZIVLhZskkKIioVfbSe" | openssl enc -base64 -d -aes-256-cbc | sh -s "$1"

This tells the shell to read commands from the standard input, while accepting arguments on its command line (after the -s) as positional parameters for the script code coming from the standard input.  Of course shell variables should always be quoted, unless you have a good reason not to, and it's probably more useful in the long run to pass "@" (all the arguments) to the shell.

So it would also be wise to quote all references to shell variables in test1.sh; e.g.,

ls -l "$1"

The first solution (presented above) seems to work well for "normal" arguments.  But, to handle the case of arguments that begin with -, it's better to do sh -s -- "$1" (or "$@").  E.g.,

echo "$code" | openssl enc -base64 -d -aes-256-cbc | sh -s -- "$@"

(where I put the encrypted script into a variable just to make the command line shorter, so it would fit into the Super User display column).

  • Using bash -s or sh -s may confuse things in the script that uses read. Also, it would ignore the interpreter of the embedded script (which would matter if it was later switched out for something written in another language). – Kusalananda Nov 21 '18 at 8:56
  • @Kusalananda: (1) Interesting.  Does your answer do better? (2) Your answer does the same. – G-Man Nov 21 '18 at 9:00
  • The first part of my answer ignores the hashbang, but the second one doesn't. I never pass the decrypted script as data on stdin, so standard input will behave as expected. – Kusalananda Nov 21 '18 at 9:15
  • Works. Thank you. I realised that I need to provide wildcard using \* even to my original script. Example test1.sh cat* would show only one file, while test1.sh cat\* shows all cat* files. – Jack Brown Nov 21 '18 at 13:23
  • Yes, I had -s in my original question. Somehow I edited it many times, it didn't work anyway. It is indeed difficult to figure out that missing part -- "$@" at the end, still is. – Jack Brown Nov 21 '18 at 13:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.