WhiteZ
WhiteZ

Reputation: 61

bash/sh nested escape sequences

I want to understand how to write nested escape sequences in sh. I have to need illustrated by an example, I want to write something like this

sh -c sh -c sh -c sh -c sh -c date

Expected Output: current date

I tried the following, could go upto 2 levels of nesting but lost:

sh -c date
Sat May  2 18:38:38 PDT 2015
sh -c "sh -c date"
Sat May  2 18:38:43 PDT 2015
sh -c "sh -c \"sh -c date\""
Unmatched ".

I want a general purpose way to escape like the 5 levels of nesting shown above.

Upvotes: 2

Views: 472

Answers (2)

Charles Duffy
Charles Duffy

Reputation: 295288

Unlike POSIX sh, bash provides an explicit way to ask the shell to escape data to be eval-safe, with printf %q.

Thus, with bash:

$ command_str=date
$ nest() { printf 'sh -c %q\n' "$1"; }
$ nest "$command_str"
sh -c date
$ nest "$(nest "$command_str")"
sh -c sh\ -c\ date
$ nest "$(nest "$(nest "$command_str")" )"
sh -c sh\ -c\ sh\\\ -c\\\ date

...and so on, ad infinitum.


In POSIX sh, the third-party library Push can be used for that same purpose:

$ source push.sh
$ command_str=date
$ Push -c command_str sh -c "$command_str"; echo "$command_str"
sh -c date
$ Push -c command_str sh -c "$command_str"; echo "$command_str"
sh -c 'sh -c date' 
$ Push -c command_str sh -c "$command_str"; echo "$command_str"
sh -c 'sh -c '\''sh -c date'\'
$ Push -c command_str sh -c "$command_str"; echo "$command_str"
sh -c 'sh -c '\''sh -c '\''\'\'\''sh -c date'\''\'\'

...etc.


If you wanted to automate the nesting for an arbitrary number of levels:

nest() {
  local cmd_str level >/dev/null 2>&1 ||: "in POSIX sh, local may not exist"
  level=$1; shift

  ## in bash
  printf -v cmd_str '%q ' sh -c "$@"; cmd_str=${cmd_str%" "}

  ## in POSIX sh, using https://github.com/vaeth/push
  #Push -c cmd_str sh -c "$@"

  if [ "$level" -gt 1 ]; then
    nest "$((level - 1))" "$cmd_str"
  else
    printf '%s\n' "$cmd_str"
  fi
}

Then:

nest 5 date

...would give you (with printf %q, vs push)...

sh -c sh\ -c\ sh\\\ -c\\\ date

Upvotes: 3

William T Froggard
William T Froggard

Reputation: 738

assuming this is what you meant...

sh -c "sh -c \"sh -c \\\" sh -c \\\\\\\" sh -c \\\\\\\\\\\\\\\" date \\\\\\\\\\\\\\\" \\\\\\\" \\\" \" "

This gets pretty ugly, and honestly I'm not sure why you'd ever want to do this, but hey, there it is. Nest away!

Upvotes: 2

Related Questions