Fish-shell: Não consigo ler o tubo para funcionar

Criado em 5 jul. 2012  ·  21Comentários  ·  Fonte: fish-shell/fish-shell

Teste:

function testfun
    set input (cat)
    echo $input
end

echo testing123 | testfun

Isso deve gerar "testing123", mas não produz nada.

Funciona perfeitamente no bash:

function testfun
{
    input="$(cat)"
    echo $input
}

echo testing123 | testfun
bug

Comentários muito úteis

Este problema, juntamente com a função grep padrão para grep , resulta em alguns problemas - Se este problema não for corrigido em breve, o alias grep padrão provavelmente deve ser removido (ou substituído por uma abreviatura, talvez?) para pelo menos minimizar as ocorrências.

Usar cat como @milieu sugere não parece resolver o problema para mim, no Fish 2.3.1 (que acabei de perceber que está um pouco atrasado, mas é a versão empacotada para o Fedora 25)

Todos 21 comentários

Você pode usar a função 'ler' como solução alternativa.

function t
    while read -l line
        echo $line
    end
end

Acabei de entender que em peixes não funciona, porque o stdin é canalizado para 'definir' em vez de 'gato'.

Esse problema é mais profundo do que apenas "definir" o trabalho de determinada maneira. A tubulação para uma função está danificada. E o I / O do terminal enviado para uma função funciona, mas ainda é um pouco estranho - parece armazenar a entrada e entregar tudo de uma vez. Observe o que acontece com esta função:

~> function meh
       cat
   end
~> # First, the way it's supposed to work.
~> # As input, we press the keys: a RET b RET control-D
~> cat
a
a
b
b
~> cat | cat
a
a
b
b
~> # Now...
~> meh
a
a
b
b
~> # So far so good, but...
~> cat | meh
a
b
^D
... um...
^D
control-D repeatedly does not work
try control-C
Job 1, “cat | meh” has stopped
~> fg
Send job 1, “cat | meh” to foreground
cat: stdin: Interrupted system call
~> jobs
jobs: There are no jobs
~> # Dear lord.
~> # For completeness...
~> meh | cat
a
b
aD
b
~> 

Além disso, cat | meh | cat se comporta da mesma maneira, assim como cat | begin; cat; end .
Posso dizer-lhe também que o "gato" que reclama sobre uma chamada de sistema interrompida em cat | meh é o primeiro "gato". Isso é:

~> cp /bin/cat mycat
~> ./mycat | meh
Job 1, “./mycat | meh” has stopped  #after control-C
~> fg
Send job 1, “./mycat | meh” to foreground
mycat: stdin: Interrupted system call

Então é isso. Obviamente, isso tem a ver com o modo como fish chama funções e como constrói tubos dentro delas. Por acaso alguém sabe sobre isso?

Ok, estou achando que está funcionando
pbpaste | begin; cat; end
repetidamente em uma concha de peixe fresco, com a área de transferência sendo "23 \ n", às vezes apenas imprime 23 de volta, e às vezes faz com que a concha trave, ponto em que o controle-C não pode fazer nada. Presumo que seja algum tipo de condição de corrida. Oh garoto.

Enquanto isso, parece que o sinal SIGTTIN é enviado para o "mycat" em ./mycat | begin; cat; end :

     21    SIGTTIN      stop process         background read attempted from
                                             control terminal

Então, de acordo com o manual GNU libc: "Um processo não pode ler no terminal do usuário enquanto está sendo executado como um trabalho em segundo plano. Quando qualquer processo em um trabalho em segundo plano tenta ler no terminal, todos os processos no trabalho são enviados um sinal SIGTTIN. "

Portanto, parece que o "mycat" é iniciado em segundo plano ou é iniciado e, em seguida, colocado em segundo plano, quando é canalizado para um tipo de função de peixe. Talvez esse conhecimento ajude.

Aparentemente, isso faz o fundo de ambos os lados de um tubo ... Mas dar o comando fg puxa o processo do fundo, permitindo que funcione como deveria.

~ $ alias pjson='python -m json.tool | pygmentize -l json'
~ $ curl -u smoku -X GET -H "Content-Type: application/json" 'https://jira.......' | pjson
Job 4, 'curl -u smoku -X GET…' has stopped
~ $ fg
Enter host password for user 'smoku': ********
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   593    0   593    0     0   1372      0 --:--:-- --:--:-- --:--:--  1375
~ $ fg
{
    "expand": "renderedFields,names,schema,transitions,operations,editmeta,changelog",
    "id": "29874"
}

Um pouco chato que eu precisei criar pjson script de wrapper em $PATH vez de um alias simples ... :(

Para minha referência, este também é o bug do openSUSE https://bugzilla.opensuse.org/show_bug.cgi?id=963548

Yay! Acho que encontrei minha solução alternativa! Graças ao comentário de gustafj na edição # 110 explicando a sintaxe de fish piping, eu vim com o seguinte:

function line --argument-names n
    cat 1>| tail -n +$n | head -1
end

Este problema, juntamente com a função grep padrão para grep , resulta em alguns problemas - Se este problema não for corrigido em breve, o alias grep padrão provavelmente deve ser removido (ou substituído por uma abreviatura, talvez?) para pelo menos minimizar as ocorrências.

Usar cat como @milieu sugere não parece resolver o problema para mim, no Fish 2.3.1 (que acabei de perceber que está um pouco atrasado, mas é a versão empacotada para o Fedora 25)

Parece que há uma diferença entre a execução dentro do shell e a partir da linha de comando:

(zsh)$ ./fish -c "read n | grep nothing"    
read> lol
(zsh)$ ./fish
(fish)$ read n | grep nothing
read> 
# Stuck forever, needs to kill the terminal. ^C, ^Z have no impact.

Talvez isso possa ajudar a depurar o problema?

@layus : Não, é o # 3805, um problema em que o próprio peixe não consegue obter o controle do terminal.

_Acho que mudou um pouco do comportamento original a este respeito, o que se segue é em relação ao Fish Master / 3.0_

Há duas questões fundamentais que fish se engana aqui, a primeira é a função de buffer / saída do bloco (tenho certeza que um shell moderno não armazenaria nada em

Em geral, você tem comandos externos (e embutidos que são efetivamente tratados da mesma forma, em geral) que são fáceis: uma entrada, duas saídas, uma das quais pode ser encadeada para o comando subsequente, a outra deve ser redirecionada para um arquivo ou o tty. Mas os blocos e as funções são complicados, pois você está basicamente mapeando uma entrada (já que só pode haver uma) para uma sequência (que eventualmente se expande para) comandos externos ou internos.

Dito isso, discordo que o comportamento atual esteja errado. (cat) não deve ler dados que foram canalizados para o comando no qual ele é executado:

mqudsi<strong i="11">@ZBook</strong> /m/c/U/m/Documents> type testfun
testfun is a function with definition
function testfun
    set input (cat)
    printf "You said '%s'\n" $input
end
mqudsi<strong i="12">@ZBook</strong> ~/r/fish-shell> echo testing123 | testfun
hello
^D
You said 'hello'

Você está canalizando a entrada para o bloco, independentemente de set consumir a entrada, consumir parte da entrada ou ignorar a entrada totalmente, cat está correto para conectar a /dev/tty para a entrada, que então é corretamente passada para o shell para substituição na linha de comando. Na verdade, existem / foram (muitos) bugs apresentados contra este repo reclamando sobre casos em que "subshells" _não_ eram lidos do terminal quando executados com alguns níveis de indireção. IMHO, é o bash que está quebrado aqui, especialmente porque o bash suporta subshells reais e oferece assincronicidade aqui.

O único comportamento quebrado, eu diria, decorre de casos em que comandos externos são iniciados em uma função / bloco e não consomem totalmente a entrada:

mqudsi<strong i="19">@ZBook</strong> /m/c/U/m/r/fish-shell> printf 'foo\nbar\n' | begin
                                        head -n1 | read -l line1
                                        head -n2 | read -l line2
                                        echo line1: $line1
                                        echo line2: $line2
                                    end
line1: foo
line2:

TBH Estou muito surpreso, mas funciona corretamente:

mqudsi<strong i="23">@ZBook</strong> /m/c/U/m/r/fish-shell> printf 'foo\nbar\n' | begin
                                        /bin/echo 'hi from echo'
                                        cat | read -z from_cat
                                        printf 'from_cat: "%s"' $from_cat
                                    end
hi from echo
from_cat: "foo
bar
"¶  

E isso também é correto:

mqudsi<strong i="27">@ZBook</strong> /m/c/U/m/r/fish-shell> printf 'foo\nbar\n' | begin
                                        cat | read -zl from_cat1
                                        cat | read -zl from_cat2
                                        printf 'from_cat1: "%s"\n' $from_cat1
                                        printf 'from_cat2: "%s"\n' $from_cat2
                                    end
from_cat1: "foo
bar
"
from_cat2: ""

Especialmente ao levar em conta o plano de algum dia introduzir subcamadas reais em peixes com execução assíncrona, eu diria que o comportamento dos peixes em relação ao caso original relatado aqui está correto. Na verdade, estou inclinado a encerrar totalmente esse problema, a menos que alguém se oponha e possa apresentar um argumento convincente aqui.

Embora o relatório de bug original seja imho inválido, as questões levantadas por @waterhouse são cat | meh relatado.

mqudsi<strong i="8">@ZBook</strong> ~/r/fish-shell> cat | meh
a
a
b
b
^D
mqudsi<strong i="9">@ZBook</strong> ~/r/fish-shell>

Dito isso, discordo que o comportamento atual esteja errado. (cat) não deve ler dados que foram canalizados para o comando em que é executado:

Eu discordo muito disso!

cat está correto para conectar a / dev / tty para entrada

Essa é uma questão de modelo mental. Eu diria que o gato se conecta ao "stdin atual" para entrada. Se a função ou bloco não for redirecionado, esse é o tty. Se for redirecionado, é isso! Portanto, conectar-se a / dev / tty aqui seria incorreto.

reclamando de casos em que "subshells" não eram lidos do terminal quando executados com alguns níveis de indireção

Observe que tudo isso era sobre substituições de comandos "globais". Por exemplo, executando echo (fzf) na linha de comando. Nesse caso, não há stdin.

Então, o que eu diria funcionaria mais ou menos assim:

echo | echo (cat) # from tty

begin
   echo | echo (cat) # from file
end < file

Há um problema relacionado (# 1035) que pergunta sobre stderr neste caso, e que não é redirecionado. O que era um grande problema com a função math antiga, porque ela apresentava uma substituição de comando dentro dela e, portanto, você não podia redirecionar isso.

Esta é a parte stdin. Se uma função executa um (cat) nua, é realmente útil ter sempre essa leitura do tty? Ou você não poderia simplesmente usar </dev/tty nesse caso?

Pensamentos interessantes.

Eu acho que tudo se resume a se os parênteses denotam uma substituição simples (ou seja, "finja que o conteúdo dos parênteses estava na linha acima, execute-os até o final, armazene o resultado em uma variável e substitua a variável aqui") ou se eles ' re (atualmente quebrado) subshells. Achei que o consenso era que os peixes não tinham o suporte adequado para a subcamada, mas a intenção sempre foi consertar isso "em algum ponto".

Se for o primeiro, então sim, eu concordo, o comportamento atual é quebrado porque se você mover o conteúdo dos parênteses para uma linha diferente, certamente deve ler a entrada sendo redirecionada para o bloco.

Mas os subshells são um conceito muito mais poderoso do que isso, e permitem que você faça coisas que não são possíveis com a substituição de comandos e crie scripts muito mais responsivos e capazes. Embora seja tecnicamente possível conectar tudo o que está sendo inserido no bloco ao stdin de um comando executado em um subshell, acho que isso seria incompatível com o modelo mental .

se os parênteses denotam substituição simples (ou seja, "finja que o conteúdo dos parênteses estava na linha acima, execute-os até o fim, armazene o resultado em uma variável e substitua a variável aqui") ou se eles são subshells (atualmente quebrados) .

Não acho que esses termos estejam definidos com clareza o suficiente para serem muito úteis aqui.

Para mim, tudo se resume ao que é mais natural, mais típico e mais útil.

Ler do terminal é certamente útil, e às vezes você deseja ler do terminal mesmo que tenha outro stdin (por exemplo, fzf faz basicamente isso exclusivamente).

Mas eu acho que a leitura de stdin é muito mais típica, especialmente considerando que os usos não interativos não lêem de tty. E como a leitura de tty ainda é possível (por meio desse redirecionamento </dev/tty ), parece correto deixar isso como a opção secundária.

O fato de não haver oposto de </dev/tty no modelo que estou sugerindo está me fazendo reconsiderar minha posição.

Posso não estar profundo o suficiente nas conchas para entender a discussão completamente. Mas preciso resolver algo e me pergunto se preciso de um script bash para resolvê-lo.

É basicamente uma tarefa muito simples: eu quero canalizar stdout de (z) cat através de pv para um mysql cli (basicamente para restaurar um backup) e porque eu não quero inserir a string de conexão, quero usar uma função para isso :

function mysqlenv --description connect to mysql server using config from .env
  mysql -u (getEnv DB_USERNAME) -p(getEnv DB_PASSWORD) (getEnv DB_DATABASE)
end

Primeiro eu tinha certeza de que isso funcionaria porque é óbvio que o stdin para um comando é stdout do comando do lado esquerdo, mas agora estou confuso. Ok, mysqlenv não é um comando, é uma função. Agora estou lendo muito texto e muito "isso deve funcionar", mas nada está funcionando.

O que eu tentei:

  • cat -|mysql... sem saída; mysql não obtém entrada; ctr + c existe mysql; tubo está funcionando em segundo plano
  • mysql... <&0 sem saída; mysql não obtém entrada; ctr + c existe mysql; tubo está funcionando em segundo plano
  • set input (cat); mysql... sem saída; mysql não obtém entrada; ctr + c existe tudo; nada permanece no fundo
  • read -z|mysql... sem saída; mysql não obtém entrada; ctr + c imprime ^c

Novamente meu prompt de comando: zcat some_backup.sql.gz|pv -s (zsize some_backup.sql.gz)|mysqlenv . Ele mostra o status do pipe quando usado diretamente com o mysql (sem uma função fish no meio) - então deve funcionar.

Então, por favor, como dar stdin da função para stdin de um comando dentro da função?

Não diga que tenho que reconectar a cada linha via while read... . Pode funcionar, mas não é uma solução, pois é muito lento para trabalhar.

@tflori : Muito mais simples. Apenas deixe o comando como está, sem qualquer redirecionamento. O problema não está nos comandos diretamente na função. Algo como

function foo
    cat
end

trabalho. O cat obtém stdin como deveria.

O que não acontece é quando está em uma substituição de comando, é quando você está encontrando esse bug.

@faho isso significa que a função inicial deve funcionar? mas isso não acontece. talvez minha versão esteja desatualizada? Estou usando 2.7.1 atualmente

talvez minha versão esteja desatualizada? Estou usando 2.7.1 atualmente

@tflori : Sim, você vai querer 3.0.2.

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

frankyan picture frankyan  ·  3Comentários

luc-j-bourhis picture luc-j-bourhis  ·  3Comentários

badp picture badp  ·  3Comentários

olivergondza picture olivergondza  ·  3Comentários

krader1961 picture krader1961  ·  3Comentários