1

I have two bash string built in commands that work fine independently but when nested generate an error message no matter what I try. Here's the two individual commands that work:

$ A="etc/.java"
$ echo $A
/etc/.java
$ B="${A//$'\057\056'/$'\057'}"
$ echo $B
/etc/java
$ B="${A^^}"
$ echo $B
/ETC/.JAVA

Now trying to combine the two commands together I get errors:

$ B="${${A^^}//$'\057\056'/$'\057'}"
bash: ${${A^^}///.//}: bad substitution
$ B="${ ${A^^}//$'\057\056'/$'\057'}"
bash: ${ ${A^^}///.//}: bad substitution
$ B="${ ${A^^} //$'\057\056'/$'\057'}"
bash: ${ ${A^^} ///.//}: bad substitution
$ B="${"${A^^}"//$'\057\056'/$'\057'}"
bash: ${"${A^^}"//'/.'/'/'}: bad substitution
$ B="${ "${A^^}" //$'\057\056'/$'\057'}"
bash: ${ "${A^^}" //'/.'/'/'}: bad substitution
$ B="${${A^^} //$'\057\056'/$'\057'}"
bash: ${${A^^} ///.//}: bad substitution

Simplified examples are presented above so one can copy and paste to their own terminal. Piping or redirection would be complicated because my real world code is this:

  while [[ $i -lt $DirsArrCnt ]] ; do
    DirsArr[$i]=false
    CurrNdx=$i
    CurrKey="${DirsArr[$(( $i + 1 ))]}"
    # ^^ = convert to upper-case
    # ${Variable//$'\041\056'/$'\041'} = Change /. to / for hidden directory sorting
    if [[ "${"${CurrKey^^}"//$'\041\056'/$'\041'}" > \
          "${"${LastKey^^}"//$'\041\056'/$'\041'}" ]] || \
       [[ "${"${CurrKey^^}"//$'\041\056'/$'\041'}" = \
          "${"${LastKey^^}"//$'\041\056'/$'\041'}" ]] ; then
        LastNdx=$CurrNdx
        LastKey="$CurrKey"
        i=$(( $i + $OneDirArrCnt))
        continue
    fi
Benjamin W.
  • 46,058
  • 19
  • 106
  • 116
WinEunuuchs2Unix
  • 1,801
  • 1
  • 17
  • 34
  • You can't nest expansions like this. On another note, wouldn't `"${A//\/.\/}"` be simpler than the octal escapes? – Benjamin W. Apr 29 '17 at 00:22
  • @BenjaminW. Well I cheated a bit to make the example simpler because I had to remap `/` to `!` because `-` is lower on the ASCII table which means directory names with `-` sort lower than subdirectories with `/` path dividers. Thanks for stating it's impossible to nest. If you want to post an answer showing setting an intermediate variable inbetween, I'll accept and upvote. – WinEunuuchs2Unix Apr 29 '17 at 00:26
  • Pretty sure this is a duplicate actually, let me check before answering ;) – Benjamin W. Apr 29 '17 at 00:27
  • I searched for an existing Q&A before posting... but I'm kind of search challenged so it may be there. – WinEunuuchs2Unix Apr 29 '17 at 00:28
  • 1
    This? http://stackoverflow.com/questions/917260/can-var-parameter-expansion-expressions-be-nested-in-bash – Benjamin W. Apr 29 '17 at 00:28
  • can you try to convert first in two steps (or whatever number of steps) before comparison? – Shiping Apr 29 '17 at 00:28
  • Oh, I remember something. Do you always want to uppercase that variable? If so, there is a possibility to do it. – Benjamin W. Apr 29 '17 at 00:30
  • @CharlesDuffy Can you reopen this? In this very case it's possible with a solution not mentioned in the canonical. – Benjamin W. Apr 29 '17 at 00:32
  • @BenjaminW., done. Would you consider editing the question's title to narrow its scope to what your answer fits? – Charles Duffy Apr 29 '17 at 00:33
  • @CharlesDuffy Will do! – Benjamin W. Apr 29 '17 at 00:34
  • @BenjaminW. The upper case is needed because when sorting the directories `/etc/X11` appears before `/etc/acpi` when you set `LANG=C` which you need to do so that `/` sorts before `-`. – WinEunuuchs2Unix Apr 29 '17 at 00:38

1 Answers1

3

In the special case of one of the expansions being upper casing, it can be done in a single expansion, using declare -u (introduced in Bash 4.0). declare -u converts to uppercase on assignment.

Combining upper casing and substitution then becomes this:

$ declare -u A='/etc/.java'
$ echo "${A//\/./\/}"
/ETC/JAVA

There is the analogous -l for lower casing and the (undocumented) -c for title casing, but these are the only cases where you can do "nested" parameter expansion.

Benjamin W.
  • 46,058
  • 19
  • 106
  • 116