LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   Can't get bash case statement to set variable (https://www.linuxquestions.org/questions/programming-9/cant-get-bash-case-statement-to-set-variable-4175596761/)

Southern Gorilla 01-04-2017 01:55 AM

Can't get bash case statement to set variable
 
Can somebody point out what I'm doing wrong here?
Code:

case ${WClass} in
        "Pale moon"|Gimp|Xfe|PhotoQt)
                Bkg="/solid"
        ;;
        *)
                Bkg=""
        ;;
esac

I know the ${WClass} is working because I have it echoing after this case statement. But $Bkg never changes regardless whether $WClass matches one of the patterns or not. I've tried putting the set in brackets like I would expect to do in a sed regexp or somewhere. I don't get any errors, just the wrong behavior.

Jjanel 01-04-2017 02:39 AM

Are you looking for/at $Bkg *after* the script exits? (It's 'gone'!)
Put an echo $Bkg as the last line, after the esac, to 'check'.
(I did that, and it 'worked' *inside* that script; but 'not' at shell prompt, after script)

http://serverfault.com/questions/336...ript-execution

Try: source yourscriptname

$ sh z
/solid
$ echo $Bkg

$ source z
/solid
$ echo $Bkg
/solid
$

Is this what is happening in you case?

Best wishes & welcome to LQ!

p.s. Oh, I added as a first line, to test: WClass="Xfe"

(not directly related, but lookup export, for *child* processes to see it)
and getting deeply obscure: http://stackoverflow.com/questions/4...-calling-shell

xode 01-04-2017 02:39 AM

You had {}'s around $WClass. I tried out the code below and it works. Also, for string constants (e.g. 'Pale moon'), I like single quotes better than double quotes but either should work.

Code:

case $WClass in
        'Pale moon'|Gimp|Xfe|PhotoQt)
                Bkg="/solid"
        ;;
        *)
                Bkg=""
        ;;
esac


Southern Gorilla 01-04-2017 04:02 AM

I made the suggested changes and still had the problem;
Code:

# Function to check if current window needs solid background
check_solids(){
        case $WClass in
                'Pale moon'|Gimp|Xfe|PhotoQt)
                        Bkg="/solid"
                ;;
                *)
                        Bkg=""
                ;;
        esac
 echo $WClass        "Bkg="$Bkg
}

And here's the resulting output;
Code:

LilyTerm Bkg=
PhotoQt Bkg=
Bkg=
Pale moon Bkg=
LilyTerm Bkg=

I thought the problem might be because it's inside a function. But the echo is inside the function as well, so that shouldn't matter. All the info I find online says the syntax I have should work, but it doesn't. Oh, that line with the missing WClass output is normal. My text editor doesn't have a window class according to xprop.

Does it matter that this function is called from a while loop? Shouldn't it still echo whatever is inside of the function regardless where it was called from? I apologize if I'm not providing enough information. The last coding I did was in BASIC on an Apple II. So I don't really know what's relevant to the problem. I thought it was just a missing set of brackets or something else with the syntax of the case statement. Obviously, it's getting the WClass information from the function that parses xprop.

I think there's something wrong with my system overall. This is not the only problem I'm having with strange behavior right now. This may be just another symptom of the larger issue.

In case it makes any difference, here's all the functional code from the script. It's all separate functions at the moment because it's easier for me to figure out little bits at a time. I plan to tie it together more cleanly once I grasp what I'm doing;
Code:

# Function to get window info
get_xprop(){
  # This first line converts a command to a variable to reduce verbosity
  Base=$(xprop -root |grep _NET_ACTIVE_WINDOW |head -1 |awk '{print $5}')
  # The next three lines get the desired values and tidy them up a bit.
  IFS=, read WInstance WClass <<<"$(xprop -id $Base WM_CLASS |cut -d "=" -f2 |tr -d '"' |sed 's/^[ \t]*//')"
  WName=$(xprop -id $Base WM_NAME |cut -d "=" -f2 |tr -d '"' |sed 's/^[ \t]*//')
  WRole=$(xprop -id $Base WM_WINDOW_ROLE |cut -d "=" -f2 |cut -d ":" -f2 |tr -d '"' |sed 's/^[ \t]*//')
}

# Function to check for change of focus
compare_focus(){
        get_xprop
        #Concatate info to create a sort of "hash" for comparison
        Conc_new=$(echo $WClass$WInstance$WName$WRole |tr -dC [:alnum:])
        if [ "$Conc_new" != "$Conc_old" ]
                then
                check_solids; Conc_old=$Conc_new
        fi
}

# Function to check if current window needs solid background
check_solids(){
        case $WClass in
                'Pale moon'|Gimp|Xfe|PhotoQt)
                        Bkg="/solid"
                ;;
                *)
                        Bkg=""
                ;;
        esac
 echo $WClass        "Bkg="$Bkg
}

# Core code needed to tie the functions together
while true; do
        compare_focus
done

For what it's worth, the script is meant to keep tabs on which workspace I'm on and switch the wallpaper if needed. Some programs are almost illegible with a fancy background bleeding through the reduced opacity. Which seems to be a bug in the compositor, it's supposed to have these windows at 99%. But that's an issue for a different forum. Anyhow, I set out to arrange for these programs to have a solid background. That will go along with a cronjob that changes the background periodically. But that's also beyond the scope of this question.

grail 01-04-2017 05:18 AM

So, normally I would just say use the good ole set -xv, however this did not help in this case, so what it was back to not only echoing variables but also including delimiters to make sure what
you have stored :)

Update to the following and all will be revealed:
Code:

echo "|$WClass|        Bkg=$Bkg"
Also, the addition or removal of {} around the variable names will make no difference. They are generally only required when you need to append something to what is stored in a variable
and you need to tell bash what the variable name is, like:
Code:

echo "start$WClassend"
echo "start${WClass}end"

In the above example the first will only show the string 'start' as you have no variable called 'WClassend', but the second will return all necessary information :)

Southern Gorilla 01-04-2017 05:56 AM

Quote:

Originally Posted by grail (Post 5650135)
So, normally I would just say use the good ole set -xv, however this did not help in this case, so what it was back to not only echoing variables but also including delimiters to make sure what
you have stored :)

Update to the following and all will be revealed:
Code:

echo "|$WClass|        Bkg=$Bkg"

I see the problem! Brilliant trick! Sed isn't cutting the leading spaces!
Code:

| LilyTerm|        Bkg=
| PhotoQt|        Bkg=
| |        Bkg=
| Pale moon|        Bkg=
| LilyTerm|        Bkg=

I still had a bit of confusion because the problem wasn't exactly where I thought it was. I had to change things up a bit, add a new variable to the read command and then echo that through sed to get rid of the offending spaces. Why doesn't the sed at the end of the 'read' line do the trick? And what can I do to make this less, um... hack? There's got to be a better way than echoing one variable into another. I know it's silly trying to polish this turd when I'm sure there are people lurking with 17 different ways to do the whole thing better. But I have to learn somehow.
Code:

get_xprop(){
  # This first line converts a command to a variable to reduce verbosity
  Base=$(xprop -root |grep _NET_ACTIVE_WINDOW |head -1 |awk '{print $5}')
  # The next three lines get the desired values and tidy them up a bit.
  IFS=, read WInstance no_class <<<"$(xprop -id $Base WM_CLASS |cut -d "=" -f2 |tr -d '"' |sed 's/^[[:space:]]*//')"
  WClass=$(echo $no_class |sed 's/^[[:space:]]*//')
  WName=$(xprop -id $Base WM_NAME |cut -d "=" -f2 |tr -d '"' |sed 's/^[[:space:]]*//')
  WRole=$(xprop -id $Base WM_WINDOW_ROLE |cut -d "=" -f2 |cut -d ":" -f2 |tr -d '"' |sed 's/^[[:space:]]*//')
}

At any rate, I now get the expected result. And I also learned something new. Just in time to go to bed.
Code:

|LilyTerm|        Bkg=
|PhotoQt|        Bkg=/solid
||        Bkg=
|Pale moon|        Bkg=/solid
|LilyTerm|        Bkg=


grail 01-04-2017 06:51 AM

This works for me:
Code:

base=$(xprop -root | awk '/^_NET_ACTIVE_WINDOW/{print $NF}')

IFS=',"' read _ WInstance _ _ WClass <<<$(xprop -id $base WM_CLASS)
#OR
IFS=, read WInstance WClass <<<$(xprop -id $base WM_CLASS | awk -F\" '{print $2","$4}')

WName=$(xprop -id $base WM_NAME | awk -F\" '{print $(NF-1)}')


Southern Gorilla 01-04-2017 07:08 AM

Quote:

Originally Posted by grail (Post 5650169)
This works for me:
Code:

base=$(xprop -root | awk '/^_NET_ACTIVE_WINDOW/{print $NF}')

IFS=',"' read _ WInstance _ _ WClass <<<$(xprop -id $base WM_CLASS)
#OR
IFS=, read WInstance WClass <<<$(xprop -id $base WM_CLASS | awk -F\" '{print $2","$4}')

WName=$(xprop -id $base WM_NAME | awk -F\" '{print $(NF-1)}')


That is so much prettier than what I had. I really need to explore awk more. I'll have to spend some time studying that so I can understand exactly what's going on. Those two variations of the read command are so wildly different from each other, and from what I had. It really shows there are a great many things I don't know yet. Thanks for taking the time to share that. Of course, I'm not sure I could bear to write three whole lines of code without using sed for something. I might have withdrawal.

gnashley 01-04-2017 08:00 AM

No need, really, for all the mawking about with grep/sed/tr/cut/awk!
Code:

#!/bin/bash

set -- $(xprop -root)
base=$5

#set $(xprop -id $base WM_CLASS)
#echo WM_CLASS=${4//\"/}

set $(xprop -id $base WM_NAME)
echo WM_NAME=${3//\"/}
set $(xprop -id $base WM_WINDOW_ROLE)
echo WM_WINDOW_ROLE=$2 $3
# or:
#echo $2 ${3/\./}

My active window (manager) doesn't seem to support WINDOW_ROLE, so I'm not sure what a hit looks like -you may need to change the last line or two.

Southern Gorilla 01-04-2017 08:05 AM

Quote:

Originally Posted by gnashley (Post 5650206)
No need, really, for all the mawking about with grep/sed/tr/cut/awk!
Code:

#!/bin/bash

set -- $(xprop -root)
base=$5

#set $(xprop -id $base WM_CLASS)
#echo WM_CLASS=${4//\"/}

set $(xprop -id $base WM_NAME)
echo WM_NAME=${3//\"/}
set $(xprop -id $base WM_WINDOW_ROLE)
echo WM_WINDOW_ROLE=$2 $3
# or:
#echo $2 ${3/\./}

My active window (manager) doesn't seem to support WINDOW_ROLE, so I'm not sure what a hit looks like -you may need to change the last line or two.

I don't have a clue how all that works. I'll definitely be studying it. Thank you very much for sharing it.

*EDIT*
WINDOW_ROLE seems to be the most commonly ignored hint. But as you can see in the output I pasted above, even window class can be ignored by some programs. That's why I gathered every bit of information my WM recognizes.

grail 01-04-2017 12:06 PM

You would need to go over the data as $5 is not the same 'base' value for me, seeing xprop -root gives a whole screen of data.

As to what is going on, the 'set' command is setting the positional variables 1 to N as if you passed all the arguments to a script and then you reference them by number, 2, 3, 4 & 5 in the example provided.

For me the base is the 13th value and here you would again need to use {} to keep the numbers grouped together, ie. ${13}, but gnashley is quite correct that parameter substitution is another good option
to stay all in bash without punching out to other commands :)

Southern Gorilla 01-04-2017 05:38 PM

Quote:

Originally Posted by grail (Post 5650329)
You would need to go over the data as $5 is not the same 'base' value for me, seeing xprop -root gives a whole screen of data.

As to what is going on, the 'set' command is setting the positional variables 1 to N as if you passed all the arguments to a script and then you reference them by number, 2, 3, 4 & 5 in the example provided.

For me the base is the 13th value and here you would again need to use {} to keep the numbers grouped together, ie. ${13}, but gnashley is quite correct that parameter substitution is another good option
to stay all in bash without punching out to other commands :)

It looks like 'grep' is still useful in this application. Xprop returns a bunch of lines and gnashley's code pulls in the first id#, which is not the right one. Using 'grep' to get the correct line would be a lot simpler than trying to cipher the value of the correct id# at the end of the 7th line. Actually, I don't think you could calculate that value consistently. Changing any of the intermediate parameters would change the value of the one you were after.
Code:

_XROOTPMAP_ID(PIXMAP): pixmap id # 0x1c00001
_NET_CURRENT_DESKTOP(CARDINAL) = 4
I3_SHMLOG_PATH(UTF8_STRING) =
I3_CONFIG_PATH(UTF8_STRING) = "/home/neanderthal/.i3/config"
I3_PID(CARDINAL) = 1274
I3_SOCKET_PATH(UTF8_STRING) = "/tmp/i3-neanderthal.eCtd0X/ipc-socket.1274"
_NET_ACTIVE_WINDOW(WINDOW): window id # 0x120012d

And it apparently collapses spaces inside quotes. Which complicates matters somewhat. There's a big difference between what 'xprop' kicks out and what that pretty code returns.
Code:

$ set $(xprop -id $base WM_NAME)
$ echo WM_NAME=${3//\"/}
WM_NAME=/home/neanderthal/Build

$ xprop -id $base WM_NAME
WM_NAME(STRING) = "/home/neanderthal/Build - LilyTerm"

That's the tricky bit about coding though, isn't it? Trying to find one procedure that safely turns all possible input into the correct output. The reason my original code was so convoluted was because I had tested those lines dozens of times to figure out how to grab exactly what I needed from what I was given. And I'm still missing stuff. But parameter substitution is my new Favorite Thing. Now that I've gotten a taste of what it can do I'll be working it into the rest of my script where I can. And I'll be delving deeper into what bash has to offer. I want to see where its limits really are.

grail 01-05-2017 01:33 AM

Glad you are getting into the joys of bash :) Just in case you do not already have it, the link below is one I fully recommend reading through as time permits:

http://mywiki.wooledge.org/TitleIndex

Southern Gorilla 01-05-2017 02:04 AM

Quote:

Originally Posted by grail (Post 5650566)
Glad you are getting into the joys of bash :) Just in case you do not already have it, the link below is one I fully recommend reading through as time permits:

http://mywiki.wooledge.org/TitleIndex

Wow! That's a whole lot of information. I should have a degree in bash by the time I finish reading all that.

Honestly, I started learning bash because it's always there and I'm always needing to do something trivial with it. I was starting to believe the folks who were telling me that bash is just a phase and I'll grow out of it and start looking for a "real" programming language. But this thread has really opened my eyes. I thought I was getting close to competent. Now I see I haven't even scratched the surface. Yes, I'm excited to see where I can go now that I know there's much more to explore than I had imagined. This is far from a "basic" language.

I did want to ask, what is the purpose of the underscores in this line?
Code:

IFS=',"' read _ WInstance _ _ WClass <<<$(xprop -id $base WM_CLASS)

astrogeek 01-05-2017 02:26 AM

And don't ignore the bash man page - a terrific resource!

Although certainly not tutorial in style, it is actually very readable, accessible and complete - a treasure among man pages!

I think a common obstacle for many is simply its length and unfamiliarity. So learn how it is organized and how to navigate quickly to major topics. Read it, one digestible section at a time to gain comfortable familiarity over time. You will be glad you did!


All times are GMT -5. The time now is 12:18 AM.