1

Я пишу небольшой сценарий bash в Linux для управления входами / выходами Pulseaudio.

Я хочу направить все входы приемников, кроме одного, в тот или иной приемник. Ввод приемника, который я не хочу направлять, это:

pi@raspberrypi:~/fonction $ pactl list sink-inputs
Sink Input #36062
    Driver: protocol-native.c
    Owner Module: 2
    Client: 198
    Sink: 2
    Sample Specification: s16le 2ch 44100Hz
    Channel Map: front-left,front-right
    Format: pcm, format.sample_format = "\"s16le\""  format.rate = "44100"  format.channels = "2"  format.channel_map = "\"front-left,front-right\""
    Corked: no
    Mute: no
    Volume: front-left: 65536 / 100% / 0.00 dB,   front-right: 65536 / 100% / 0.00 dB
            balance 0.00
    Buffer Latency: 120000 usec
    Sink Latency: 236349 usec
    Resample method: n/a
    Properties:
        media.name = "ALSA Playback"
        application.name = "ALSA plug-in [snapclient]"
        native-protocol.peer = "UNIX socket client"
        native-protocol.version = "32"
        application.process.id = "539"
        application.process.user = "snapclient"
        application.process.host = "raspberrypi"
        application.process.binary = "snapclient"
        application.language = "C"
        application.process.machine_id = "69e523231bb44f2e926a758a63cbb5b1"
        module-stream-restore.id = "sink-input-by-application-name:ALSA plug-in [snapclient]"

Чтобы переместить вход приемника в другой приемник, мне нужно использовать эту команду:

pactl move-sink-input <sink-input id> <sink id>

Поэтому я должен проанализировать результат первой команды, чтобы получить Sink Input #ID (содержится в первой строке), но я должен использовать условие, чтобы проверить, является ли module-stream-restore.id (последняя строка) действительно тем, что я хочу по маршруту.

Мне нужно что-то вроде:

if [ <last line> = 'sink-input name I need' ] 
then
  pactl move-sink-input <id from first line> <my sink name>
fi

Я просто не знаю, как разобрать команду, чтобы получить ОБА информацию.

Прямо сейчас я могу получить только последнюю строку или только #ID.

 pactl list short sink-inputs|while read stream; do
    streamId=$(echo $stream )
    id=$(echo pactl list sink-inputs | grep module-stream-restore.id | grep -o '".*"' |sed 's/"//g')

    if [ "$streamId" != 'sink-input-by-application-name:ALSA plug-in [snapclient]' ]
    then
        pactl move-sink-input "$streamId" Snapcast
    fi

Как мне приступить к анализу результата входных данных pactl list sink-inputs чтобы я мог прочитать два элемента, а не только один, в каждом "блоке" (иначе говоря, каждый входной приемник) результата этой команды в сценарии bash?

2 ответа2

2

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

Простое решение - сохранить вывод списка во временный файл, а затем дважды отсканировать его:

pactl list short sink-inputs >temp.lst
streamId=$(sed -n 's/^Sink Input #\(.*\)$/\1/p' <temp.lst)
id=$(sed -n 's/.*module-stream-restore.id = "\(.*\)"$/\1/p' <temp.lst)
rm temp.lst 

Теперь у вас есть две переменные, которые вам нужно установить. Это не очень элегантное решение, но оно простое и легкое для понимания и поддержки. Обратите внимание, что я использовал sed для выполнения функций grep : руководство по тому, как это работает, смотрите здесь.

Однако вы можете избежать временного файла с более запутанным решением:-

Ids="$(pactl list short sink-inputs | \
       sed -n -e 's/^Sink Input #\(.*\)$/\1/p' \
              -e 's/.*module-stream-restore.id = "\(.*\)"$/\1/p')"

Теперь у вас есть обе переменные в Ids , разделенные новой строкой: вы можете разделить их с помощью: -

streamId="${Ids%$'\n'*}"
Id="${Ids#*$'\n'}"

Если вам не нравятся временные переменные, вы можете использовать Id вместо Ids !

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

2

Концепт:

  1. pactl … с LC_ALL=C установленным, чтобы избежать локализованного вывода (вам, очевидно, это не нужно, но в общем случае люди это делают).
  2. Используйте egrep чтобы удалить ненужные строки. Вы хотите получить пары строк, которые выглядят так:

    Sink Input #12345
            module-stream-restore.id = "whatever"
    
  3. Предполагая, что строки идут парами, read их два на два с надлежащим IFS (# для первой строки в паре, = для второй), чтобы извлечь соответствующие данные. Используйте фиктивные переменные для деталей, которые вам не нужны.

  4. Работа с извлеченными данными. Обратите внимание, что IFS='=' для второй строки в паре извлечет все после = , то есть соседний пробел и двойные кавычки. Вот почему в коде (ниже) я ' "sink-… [snapclient]"' , а не просто с 'sink-… [snapclient]' .

Код:

LC_ALL=C pactl list sink-inputs |
egrep "^Sink Input #|module-stream-restore.id = " |
while IFS='#' read dummy id && IFS='=' read dummy restid; do
 if [ "$restid" = ' "sink-input-by-application-name:ALSA plug-in [snapclient]"' ] ; then
  printf 'Match for ID %s.\n' "$id"
  # do something
 else
  printf 'No match for ID %s.\n' "$id"
  # do something else
 fi
done

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .