Analisando o resultado de um comando em um script bash do Linux

1

Estou escrevendo um pequeno script no Linux para controlar as entradas / saídas do Pulseaudio.

O que eu quero fazer é rotear todas as entradas de sumidouro, exceto uma, em uma ou outra pia. A entrada sink que eu não quero rotear é essa:

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]"

Para mover uma entrada sink para outro coletor, eu preciso usar este comando:

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

Então eu tenho que analisar o resultado do primeiro comando para obter o Sink Input #ID (contido na primeira linha), mas eu tenho que usar uma condição para verificar se module-stream-restore.id (última linha) é realmente o que eu quero rota.

Eu preciso de algo como:

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

Eu simplesmente não sei como analisar o comando para obter AMBAS informações.

Neste momento, posso obter apenas a última linha ou apenas o #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

Como faço para analisar o resultado de pactl list sink-inputs para que eu possa ler dois elementos, não apenas um, em cada "bloco" (também conhecido como cada entrada sink) do resultado desse comando em um script bash?

    
por Gob Tron 07.02.2018 / 13:54

2 respostas

2

O problema é que as variáveis definidas no loop do não são mantidas de uma passagem para a próxima.

A solução simples é salvar a saída da lista em um arquivo temporário e digitalizá-la duas vezes: -

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

Agora você tem as duas variáveis que você precisa definir. Não é uma solução muito elegante, mas é simples e fácil de entender e manter. Observe que usei sed para executar as funções grep : para um guia de como isso funciona, consulte aqui .

No entanto, você pode evitar o arquivo temporário com uma solução mais complicada: -

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

Agora você tem as duas variáveis em Ids , separadas por uma nova linha: você pode dividi-las com: -

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

Se você também não gosta de variáveis temporárias, pode usar Id em vez de Ids !

Eu testei isso copiando a saída da lista e usando isso em vez de um comando pactl real, então tudo deve funcionar com um comando ao vivo.

    
por 07.02.2018 / 22:10
2

O conceito:

  1. Invoque pactl … com LC_ALL=C set para evitar a saída localizada (aparentemente, você não precisa disso, mas em geral as pessoas fazem isso).
  2. Use egrep para descartar linhas irrelevantes. Você deseja obter pares de linhas com esta aparência:

    Sink Input #12345
            module-stream-restore.id = "whatever"
    
  3. Supondo que as linhas vão em pares, read as duas por duas com IFS ( # para a primeira linha em um par, = para a segunda) para extrair dados relevantes. Use variáveis dummy para peças que você não precisa.

  4. Trabalhe com dados extraídos. Observe que IFS='=' para a segunda linha em um par extrairá tudo após o = , ou seja, o espaço adjacente e aspas duplas também. É por isso que no código (abaixo) eu correspondi com ' "sink-… [snapclient]"' , não apenas 'sink-… [snapclient]' .

O código:

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
    
por 07.02.2018 / 23:34