zsh assignment failed dentro da função canalizada

4

Existe um caso de teste simples

local testa=("a")
local testb=("b")

function test { testb=(${(P)1}) }

test "testa"
echo "testb="$testb

saída testb=a e

local testa=("a")
local testb=("b")

function test { testb=(${(P)1}) }

test "testa" | cat
echo "testb="$testb

a saída é testb=b

Eu suponho que a saída deve ser testb=a também.

O que está errado?

    
por Ezhik 26.03.2017 / 17:00

2 respostas

2

O pipe introduz um subshell no qual alterações de variáveis são mascaradas do pai; isso é semelhante a while faz um loop envolvendo pipes onde as mudanças de variáveis dentro desse loop não afetam o pai . A alteração do PID pode ser observada com o módulo zsh/system :

zmodload zsh/system

local testa=("a")
local testb=("b")

function test {
    testb=(${(P)1})
    echo "INSIDE testb=$testb $sysparams[pid]"
}

test "testa" | cat
echo "OUTSIDE testb=$testb $sysparams[pid]"

Que deve mostrar algo como

% zsh foo.zsh
INSIDE testb=a 61488
OUTSIDE testb=b 61487
% 

subshell diferente, diferentes variáveis.

    
por 26.03.2017 / 17:17
2

Os dois lados do operador de tubulação correm em paralelo. Isso é feito executando os dois lados em processos separados. (Era a única maneira de fazer isso no shell original do Unix, e ainda é a única maneira de fazer isso nos shells modernos.) Considere um trecho como

testb=left | testb=right
echo $testb

Em zsh, o lado direito de um operador de pipe é executado no processo original, e o lado esquerdo é executado em um subshell . Um subshell é normalmente implementado através de um processo separado; mesmo que o shell otimize a execução e não use um processo separado, ele garante que qualquer modificação no estado do shell (variáveis, redirecionamento, etc.) fique confinada ao subshell, para consistência. Assim, o snippet acima imprime right .

(Outros shells também podem ser executados do lado direito do pipe em uma subshell. Entre os shells comuns, apenas zsh e ATT ksh executam o lado direito no contexto original. O lado esquerdo sempre é executado em um subnível.)

Se você deseja enviar dados sem criar um subshell, pode usar uma substituição de processo . Uma substituição de processo cria um pipe em um subshell e controla se o contexto original está conectado ao lado de entrada ou ao lado de saída do pipe.

test "testa" > >(cat)

(Observe que o espaço entre > e >(…) : >>(cat) seria analisado como uma substituição de processo no modo append, que passaria um caminho para o pipe como um segundo argumento para test . Aqui queremos para redirecionar a saída da chamada de função para o pipe.)

    
por 27.03.2017 / 01:55