I would like to use variable substitution on a particular string that I access via a command. For example, if I copy something into my clipboard, I can access it like this.

$ xclip -o -selection clipboard
Here's a string I just copied.

If I assign it to a variable, then I can do variable substitution on it.

$ var=$(xclip -o -selection clipboard)
$ echo $var
Here's a string I just copied.
$ echo ${var/copi/knott}
Here's a string I just knotted.

However, is there a way to do variable substitution without assigning it to a variable? Conceptually, something like this.

$ echo ${$(xclip -o -selection clipboard)/copi/knott}
bash: ${$(xclip -o -selection clipboard)/copi/knott}: bad substitution

This syntax fails, because var should be a variable name, not a string.


No, you can't. bash and most other shells (except zsh) don't allow nested substitution.

With zsh, you can do nested substitution:

$ echo ${$(echo 123)/123/456}   
  • I'll accept this answer, since it provides some circumstantial evidence that it's not possible in bash. (And pushes me again towards migrating to zsh.) – Sparhawk Mar 12 '15 at 1:13

Yeah, you can do that - kind of. It's really not pretty. It's more like in-line than nested. The problem is you have to operate on the value of the parameter you expand - if that parameter has no value then you won't do much. So, you can assign the value while expanding it and it's hardly a shortcut.

v=; echo "${v:=${0##*["$0${v:=$(xsel -bo)}"]}${v/copi/knott}}"

I use the $0 param expansion within the chain to hide the assignment. It assigns the var's value within a nested assignment expansion. The outer takes precedence - but because it would just expand to whatever the inner one does it's hard to tell. However, if we silence the inner expansion, then modify it you can get what you want. After copying your string to my clipboard (I don't have xclip - just xsel) it prints:

Here's a string I just knotted.

It's a little clearer what's going on if you leave $0 out, though:

v=; echo "${v:=${v:=$(xsel -bo)}${v/copi/knott}}"

That prints:

Here's a string I just copied.  Here's a string I just knotted.

...because the inner assignment occurs before the modification, but, as noted, the outer assignment takes precedence - and it expands to both the inner-assignment's expansion and to the modified inner-expansion.

Of course none of that works at all if the targeted parameter is already assigned - so you can only do it surely if you empty the variable in the first place... which, honestly, is probably the most convenient time to assign it after all.

  • +1 for the workaround, although as you say, it's a workaround that probably worse than assigning a variable! – Sparhawk Mar 12 '15 at 1:11
  • @Sparhawk - yeah, definitely worse. And there's really nothing wrong w/ that anyway - there's not much to gain except uncertainty. You could come up with some alias indirection to make it a little more convenient - but if its worth it to you then you should setup a function for handling safe-quoting and doing something w/ eval or something like it anyway. w/ eval - if you can make the first chars of the command sub's output amount to workable expansion syntax - then you can probably get a lot further a lot easier. I know such a thing would be easy w/ xsel - it takes stdin - but xsel? – mikeserv Mar 12 '15 at 2:19
  • @Sparhawk - I only know how to do any of that, by the way, because in some situations it can be useful - such as prompt or here-doc expansions - in which you can't get a current shell assignment to apply otherwise. – mikeserv Mar 12 '15 at 2:23

If you don't want to create a variable, then there are other ways to perform string substitution:

$ echo "$(xclip -o -selection clipboard | sed 's/copi/knott/')"
Here's a string I just knotted.
  • Thanks, I did know that I could use sed instead, but it was meant to be more of a general question, on nesting of substitutions. – Sparhawk Mar 11 '15 at 3:29
  • @Sparhawk To my knowledge, one cannot do variable substitution without having a variable. – John1024 Mar 11 '15 at 3:32
  • Okay, that's probably the answer then. I'll leave the question open for a few days to see if anyone else has a referenced answer, then accept this one otherwise. Thanks. – Sparhawk Mar 11 '15 at 3:34
  • @Sparhawk Very good. – John1024 Mar 11 '15 at 3:35
  • +1, but I'm going to accept another answer, since it provides slightly more concrete circumstantial evidence that it doesn't work in bash. – Sparhawk Mar 12 '15 at 1:12

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.