Retornando uma String e Compreendendo o Redirecionamento e a Saída

3

Estou tentando descobrir como os redirecionamentos podem ser usados para a saída seletiva de uma string de uma função bash que precisa imprimir várias linhas no terminal. Eu encontrei uma resposta sobre esta pergunta no StackOverflow . O código abaixo funciona. Mas eu não entendo como ou por quê.

#!/bin/bash

exec 3>&1

returnString()
{
    exec 4>&1 >&3
    local s=$1
    s=${s:="some default string"}
    echo "writing to stdout"
    echo "writing to stderr" >&2
    exec >&4-
    echo "$s"
}

my_string=$(returnString "$*")
echo "my_string:  [$my_string]"

Eu entendo que o fd3 é redirecionado para stdout no começo. Então, fd4 é também, e 1 é apontado para 3, que basicamente aponta de volta para si mesmo. Mas como isso afeta o que é impresso no terminal, e por que essa saída não aparece em $my_string , mas a última depois de se livrar do fd4 é?

Eu não entendo o que o fd4 tem a ver com nada, já que nada é explicitamente enviado para o fd4.

    
por BryKKan 18.03.2016 / 19:00

2 respostas

3

A solução que você está mostrando acima é uma que é de propósito geral e abrange muitos casos. A menos que você saiba que tem um problema, você pode querer adotar uma postura YAGNI (Você não vai precisar disso) e apenas redirecionar suas linhas selecionadas para / dev / tty.

Se você quiser evitar isso, então você precisa entender o malabarismo do descritor de arquivos (fd) que está acontecendo.

O 3>&1 está copiando o stdout do contexto de chamada para uma "célula de retenção" para que a função possa ser enviada para o stdout do contexto de chamada. Nesse caso, é / dev / tty. Lembre-se, a função será chamada com stdout pertencente a um subshell para a substituição $ {}.

O exec 4>&1 cria uma cópia do fd1, que é o stdout da função. O fd4 está sendo usado como uma célula de retenção para o stdout com o qual a função começa. O >&3 está definindo o stdout da função / subshell para o stdout do chamador. Assim, toda a saída irá para o stdoout do chamador. UNTIL, o final em que o >&4- move o stdout salvo de volta para o início, permitindo que o último echo "$s" esteja no stdout da função (e subshell).

Ufa!

Os >&3 e >&4- também podem ser escritos com mais clareza como 1>&3 e 1>&4- .

A seção manual bash em REDIRECTION, explica todos os detalhes de & gory & gory; nomenclatura. Fiquei satisfeito e surpreso ao ver que é realmente possível usar formulários {name} para alterar os números para palavras.

"Cada redirecionamento que pode ser precedido por um número de descritor de arquivo pode ser precedido por uma palavra da forma {varname}. Nesse caso, para cada operador de redirecionamento, exceto > & - & < & -, o shell alocará um descritor de arquivo maior que 10 e o atribuirá a varname. Se > & - ou < & - for precedido por {varname}, o valor de varname definirá o descritor de arquivo para fechar. "

Eu não testei isso, mas sugere que você poderia codificar isso como:

#!/bin/bash

exec {caller_stdio}>&1

returnString() {
  exec {func_stdio}>&1 1>&{caller_stdio}
  local s=$1
  s=${s:="some default string"}
  echo "writing to stdout"
  echo "writing to stderr" >&2
  exec 1>&{func_stdio}-
  echo "$s"
}

my_string=$(returnString "$*")
echo "my_string:  [$my_string]"

Espero que isso ajude.

    
por 18.03.2016 / 19:48
3
exec 3>&1
O

fd 3 é agora um dup do fd 1 (no seu exemplo, o seu terminal). Como diz a página man, isso significa que fd 1 e fd 3 podem ser usados de forma intercambiável para se referir ao mesmo arquivo ou dispositivo.

O principal uso de duping no shell é salvar uma cópia de um fd para que ele possa ser restaurado posteriormente.

my_string=$(returnString "$*")

Para que o bash obtenha a saída stdout da avaliação de returnString "$*" , ele cria um tubo e garfos, e a criança chama dup2 para mover a extremidade da gravação do canal ( neste exemplo, é fd 5, mas isso pode variar) para fd 1.

exec 4>&1 >&3

Os redirecionamentos são avaliados da esquerda para a direita. fd 4 está definido para ser um dup da extremidade do tubo. Então, fd 1 é ajustado para ser um dup do fd que se refere ao seu terminal.

echo "writing to stdout"

ecoa para o fd 1, o seu terminal.

exec >&4-

Isso se move (isto é, faz um dup2) de fd 4 para fd 1. fd 1 é agora o final do tubo.

echo "$s"

ecoa na extremidade do tubo. Eventualmente, isso é lido pelo shell pai da outra extremidade do pipe e usado como resultado de $(returnString "$*")

    
por 18.03.2016 / 20:00