7

Is it possible to combine two or more string manipulation functionalities of a variable in BASH (or any other standard Linux command)?

Let's say e.g. I have the variable $XDG_CURRENT_DESKTOP which holds the string ubuntu:GNOME.
Now, I can retrieve the second substring by ${XDG_CURRENT_DESKTOP##*:}GNOME.
I can also retrieve the lower case string through ${XDG_CURRENT_DESKTOP,,}ubuntu:gnome.

But how can I combine both functions (→ gnome) in onesimple – command without using a redirection to sed, awk, grep or any other of these – quite heavy weighted – commands and without an additional buffer storage step? e.g.:

local mybuffer="${XDG_CURRENT_DESKTOP##*:}"
echo "${mybuffer,,}"

I want to avoid such a "sub-script" or function call construct to achieve this and I already tried any combination of both mentioned but it seems to be futile.
Is there any other way?
Or do I need to upgrade bash? (using: GNU bash 4.3.11)
Or can any other shell do that?

4
  • 1
    zsh can do it, with the L parameter expansion flag ex. ${(L)XDG_CURRENT_DESKTOP##*:} Nov 25, 2021 at 18:44
  • @steeldriver: Oohhh, nice! THAT is exactly what I was looking for! Time for zsh. Thank you very much! :))
    – Jens
    Nov 26, 2021 at 1:52
  • @steeldriver may as well post that as an answer since the OP also asked for other shells.
    – terdon
    Nov 27, 2021 at 15:19
  • @terdon: d'accord
    – Jens
    Nov 28, 2021 at 0:10

2 Answers 2

4

You could do this in bash if you use read to read two variables:

$ echo "$XDG_CURRENT_DESKTOP"
ubuntu:GNOME
$ IFS=':' read var1 var2 <<<"${XDG_CURRENT_DESKTOP@L}"
$ echo "$var2"
gnome

The ${variable@L} construct returns the value of $variable converted to lower case. Then, IFS=':' read sets the input field separator to : for the read command, this way the global IFS is left unchanged after the command exits, and then read var1 var2 will separate its input on : and save the result in the two variables var1 and var2. Note that if you have more than one : on the same line in the input, var1 will have everything up to the first : and var2 everything else. Finally, <<< is a "here string", a simple way to pass a variable as input to a command.

5
  • Nice workaround! But unfortunately this construct is way to long and presumably with to many background processes (→ KISS principle). BTW, with this bash version I think I can adopt the combination similar to the zsh proposal from @steeldriver above. Anyway, 1+ for the inspiration!
    – Jens
    Nov 28, 2021 at 0:08
  • @Jens it is long and unwieldy to write, absolutely. Zsh tends to be more elegant with this sort of thing. But many background processes? Why? This is all 100% bash builtin tools, I don't think there will be much (any?) forking involved.
    – terdon
    Nov 28, 2021 at 0:38
  • Good point! I'm just trying to implement something like an "uniform directive/constant" that really shouldn't take to much coding and background processing. Meanwhile I anyway came to the point that zsh might be counterproductive due to the fact that it seems to be not widely distributed. So it doesn't make much sense.
    – Jens
    Nov 30, 2021 at 18:32
  • @Jens zsh is becoming more and more popular, and is the default shell on some non-Ubuntu systems (macOS as far as I know, but maybe others) but yes, you cannot assume it will be available. But then you also cannot assume that bash is available :). For true portability, you need to stick to a basic POSIX shell like sh or dash. My answer depends on bashisms, the portable way would be to pipe to a portable external tool like sed or perl. All of this is off topic here though, we only handle Ubuntu. You might want to check out Unix & Linux for portability questions.
    – terdon
    Nov 30, 2021 at 18:37
  • sh, dash, bash, zsh, etc. … too many choices … I think I lost track of the global view with this detail ;oP I must admit, I did not consider this shell issue yet. Maybe for now I should stay with bash. From this perspective your code looks much better the more I think about it xD
    – Jens
    Nov 30, 2021 at 22:52
0

Here's a bash5 (not bash4) script:

part2() {
   local -n var=$1
   var=${var#*:}
}
lc() {
   local -n var=$1
   var=${var,,}
}
fred="ubuntu:gnome"
part2 fred
lc fred
printf "%s\n" "$fred"

Output:

gnome

The functions part2 and lc modify the variable whose name is passed to them, without forking. In the end, the original variable (fred) has been modified. You still need the extra variable though.

The alternative, which should work on bash4, is something like this:

part2() { printf "%s" "${1#*:}"; }
lc()    { printf "%s" "${1,,}" ; }
fred="ubuntu:GNOME"
printf "%s\n" "$(lc "$(part2 "$fred")")"

which does not modify any variables (it only manipulates values), but it does create extra forks.

1
  • First of all: thank you for your effort. The result is fine. FYI: all of your code is Bash4 in deed ;-). I really do appreciate modular programming, but in this case I was more looking for a single command code or alternatively a one-liner. Sorry, but thank you very much anyway.
    – Jens
    Dec 3, 2021 at 22:16

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.