Выполнить команду из переменной внутри цикла while или экспортировать переменную из цикла while

#bash #shell #while-loop #moonlight

#bash #оболочка #цикл while #лунный свет

Вопрос:

Я написал скрипт с циклом while, не понимая сложности получения переменной из указанного цикла while.

Что я хочу сделать, так это запустить команду, которую цикл while сохраняет в переменной ‘stream’ (см. Код ниже) после того, как цикл while завершит столько циклов, сколько необходимо. Я использую этот скрипт для определения того, какие игровые контроллеры подключены, и создаю команду для инициализации moonlight, программы для raspberry pi, которая позволяет мне транслировать через Nvidia Game Stream с различными типами контроллеров.

 #!/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'
 

Я понимаю, что это, вероятно, не самый чистый способ сделать это или что код даже близко написан так хорошо, как мог бы быть, но поскольку у меня мало предыдущего опыта в кодировании любого рода, это то, что я мог бы сделать на данный момент.

Любая помощь будет принята с благодарностью!

Комментарии:

1. Проверьте информацию wooledge wiki или shellcheck по этой проблеме.


Ответ №1:

Ну, способ запустить ее как команду — просто удалить echo :

 done
$stream
 

… за исключением того, что вы не можете, потому while что цикл находится на правой стороне канала. Это означает, что он выполняется в «подоболочке» и не может иметь побочных эффектов в своей родительской оболочке (вне цикла); после завершения цикла $stream он вернется к тому, что было до цикла.

Чтобы сохранить цикл в той же среде, что и окружающий код, а не в его собственной подоболочке, вам нужно читать из файла напрямую, а не использовать канал:

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

В общем случае при динамическом создании команды для последующего запуска вы столкнетесь с проблемами с кавычками, пробелами или другими специальными символами. Проще всего избежать этих проблем, если вы создаете команду в массиве, а не в строке. Синтаксис для построения и выполнения команды внутри массива выглядит следующим образом:

 stream=(moonlight stream -1080 -app Steam)
...
while read CMD; do
  ...
  stream =("$XbElite" "$Player$event")
  ...
  stream =("$PlSt3" "$Player$event")
...
done </proc/bus/input/devices
"${stream[@]}"
 

Комментарии:

1. Проблема, с которой я сталкиваюсь при этом, заключается в том, что как только цикл While заканчивается, переменная stream больше не изменяется, как это происходит внутри цикла.

2. Да, упустил из виду, что был канал. См. Редактирование — я только что добавил объяснение и исправление для этого.

3. Затем я получаю $stream, который затем записывается как: ‘moonlight stream -1080 -app Steam’ без добавления конфигураций контроллера, добавленных через цикл while.

4. похоже, вы все еще используете cat | . Нет каналов.

Ответ №2:

То, что сказал Марк Рид, а также основной цикл можно упростить:

 sed 's/([-:( )=])/\amp;/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