0

I've written a script with a while loop, not realising the difficulty of getting a variable out of said while loop.

What I want to do is to run the command the while loop saves in the 'stream' variable (see code below) after the while loop has completed as many loops as needed. I'm using this script to detect which game controllers are connected and creating a command to initialize moonlight, a program for the raspberry pi which lets me stream through Nvidia Game Stream, with different controller types.

#!/bin/bash

stream="moonlight stream -1080 -app Steam"
device=none

event=0

Player="-input /dev/input/event"

XbElite="-mapping /opt/retropie/configs/moonlight/XboxOne.map"
PlSt3="-mapping /opt/retropie/configs/moonlight/PS3.map"

XboxElite="N\:\ Name\=\"Microsoft\ X\-Box\ One\ Elite\ pad\""
PS3="N\:\ Name\=\"PLAYSTATION\(R\)3\ Controller\""

cat /proc/bus/input/devices | \
while read CMD; do
    line=$(echo "$CMD" | sed 's/ /\\ /g; s/:/\\:/g; s/(/\\(/g; s/)/\\)/g; s/-/\\-/g; s/=/\\=/g')
    if [ "$line" = "$XboxElite" ]; then
        echo Xbox
        device=xboxElite
        if [ "$device" = "xboxElite" ]; then
            stream="$stream $XbElite $Player$event"
            event=$((event+1))
            device=none
        fi
    elif [ "$line" = "$PS3" ]; then
        echo PS3
        device=ps3
        if [ "$device" = "ps3" ]; then
            stream="$stream $PlSt3 $Player$event"
            event=$((event+1))
            device=none
        fi
    fi
done
echo $stream #put here as a placeholder to see the final command in the variable. Currently printing: 'moonlight stream -1080 -app Steam'

I realise this probably isn't the cleanest way of doing this or that the code is even close to as well written as it could be, but as I have little previous experience with coding of any sort, this is what I could manage for the time being.

Any help would be greatly appreciated!

1

2 Answers 2

1

Well, the way to run it as a command would be to just remove the echo:

done
$stream

... except that you can't, because the while loop is on the right side of a pipe. That means it runs in a "subshell" and can't have side effects back in its parent shell (outside of the loop); after the loop exits, $stream will be back to what it was before the loop.

In order to keep the loop in the same environment as the surrounding code instead of its own subshell, you need to read from the file directly rather than using a pipe:

# no `cat` command here.  No pipe.
while read CMD; do
...
done </proc/bus/input/devices #this replaces the `cat |`.
$stream

In the general case, when building a command dynamically to run later, you are going to run into problems with quoting or spaces or other special characters. It's easiest to avoid those issues if you build the command in an array instead of a string. The syntax for building and running a command inside an array looks like this:

stream=(moonlight stream -1080 -app Steam)
...
while read CMD; do
  ...
  stream+=("$XbElite" "$Player$event")
  ...
  stream+=("$PlSt3" "$Player$event")
...
done </proc/bus/input/devices
"${stream[@]}"
Sign up to request clarification or add additional context in comments.

4 Comments

The issue I get with doing this, is that as soon as the While-loop ends, the stream variable is no longer modified as it is when inside the loop.
Yup, overlooked that there was a pipe. See edit - I just added the explanation and fix for that.
I then get the $stream is then written as: 'moonlight stream -1080 -app Steam' without the addition of the controller configs added through the while loop.
that sounds like you're still using cat |. No pipes.
0

What Mark Reed said, and also the main loop can be simplified:

sed 's/\([-:\( )=]\)/\\&/g' /proc/bus/input/devices | \
while read line; do
    if [ "$line" = "$XboxElite" ]; then
        echo Xbox
        stream="$stream $XbElite $Player$event"
        event=$((event+1))
    elif [ "$line" = "$PS3" ]; then
        echo PS3
        stream="$stream $PlSt3 $Player$event"
        event=$((event+1))
    fi
done 

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.