Fish-shell: Impossible de lire le tube vers la fonction

Créé le 5 juil. 2012  ·  21Commentaires  ·  Source: fish-shell/fish-shell

Tester:

function testfun
    set input (cat)
    echo $input
end

echo testing123 | testfun

Cela devrait afficher "testing123", mais ne produit rien.

Cela fonctionne parfaitement en bash:

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

echo testing123 | testfun
bug

Commentaire le plus utile

Ce problème, associé à la fonction grep par défaut pour grep , entraîne de nombreux problèmes - Si ce problème ne sera pas résolu de sitôt, l'alias grep par défaut devrait probablement être supprimé (ou remplacé par une abréviation, peut-être?) pour au moins minimiser les occurrences.

Utiliser cat comme @milieu le suggère ne semble pas résoudre le problème pour moi, sur Fish 2.3.1 (dont je viens de réaliser qu'il est légèrement en retard, mais c'est la version packagée pour Fedora 25)

Tous les 21 commentaires

Vous pouvez utiliser la fonction «lecture» comme solution de contournement.

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

Je viens de comprendre que dans le poisson, cela ne fonctionne pas, car le stdin est réglé sur 'set' au lieu de 'cat'.

Ce problème va plus loin que le simple fait de "définir" le fonctionnement d'une certaine manière. La tuyauterie dans une fonction est foutue. Et les E / S du terminal envoyées dans une fonction fonctionnent, mais restent un peu bizarres - elles semblent mettre l'entrée en mémoire tampon et la livrer en même temps. Observez ce qui se passe avec cette fonction:

~> 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
~> 

De plus, cat | meh | cat se comporte de la même manière, tout comme cat | begin; cat; end .
Je peux vous dire plus loin que le "chat" qui se plaint d'un appel système interrompu dans cat | meh est le premier "chat". C'est:

~> 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

Alors voilà. De toute évidence, cela a à voir avec la façon dont le poisson appelle les fonctions et comment il y construit des tuyaux. Quelqu'un est-il au courant de cela?

Ok, je trouve que courir
pbpaste | begin; cat; end
à plusieurs reprises dans une coquille de poisson fraîche, avec le presse-papiers étant "23 \ n", imprimera parfois 23 simplement, et provoquera parfois le verrouillage de la coquille, auquel point control-C ne peut rien faire. Je suppose que cela doit être une condition de course. Oh mec.

Pendant ce temps, il semble que le signal SIGTTIN soit envoyé au "mycat" dans ./mycat | begin; cat; end :

     21    SIGTTIN      stop process         background read attempted from
                                             control terminal

Ensuite, selon le manuel de la libc GNU: "Un processus ne peut pas lire depuis le terminal de l'utilisateur pendant qu'il s'exécute en tant que travail en arrière-plan. Lorsqu'un processus dans un travail en arrière-plan tente de lire à partir du terminal, tous les processus du travail sont envoyés un signal SIGTTIN. "

Donc, on dirait que "mycat" démarre en arrière-plan, ou est démarré puis mis en arrière-plan, quand il est redirigé vers une fonction de poisson. Peut-être que cette connaissance vous aidera.

Cela fait un arrière-plan des deux côtés d'un tube apparemment ... Mais donner la commande fg tire le processus de l'arrière-plan, ce qui lui permet de fonctionner comme prévu.

~ $ 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"
}

Un peu ennuyeux d'avoir besoin de créer pjson script wrapper $PATH au lieu d'un simple alias ... :(

Pour ma référence, il s'agit également du bogue openSUSE https://bugzilla.opensuse.org/show_bug.cgi?id=963548

Yay! Je pense que j'ai trouvé ma solution de contournement! Grâce au commentaire de gustafj dans le numéro 110 expliquant la syntaxe de la tuyauterie de poisson, j'ai trouvé ceci:

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

Ce problème, associé à la fonction grep par défaut pour grep , entraîne de nombreux problèmes - Si ce problème ne sera pas résolu de sitôt, l'alias grep par défaut devrait probablement être supprimé (ou remplacé par une abréviation, peut-être?) pour au moins minimiser les occurrences.

Utiliser cat comme @milieu le suggère ne semble pas résoudre le problème pour moi, sur Fish 2.3.1 (dont je viens de réaliser qu'il est légèrement en retard, mais c'est la version packagée pour Fedora 25)

Il semble qu'il y ait une différence entre l'exécution dans le shell et depuis la ligne de commande:

(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.

Peut-être que cela peut aider à déboguer le problème?

@layus : Non, c'est # 3805, un problème où le poisson lui-même ne peut pas prendre le contrôle du terminal.

_Je pense qu'une partie du comportement d'origine à cet égard a changé, ce qui suit concerne le fish master / 3.0_

Il y a deux problèmes fondamentaux que le poisson se trompe ici, le premier est la mise en mémoire tampon de la fonction / sortie du bloc (je suis presque sûr qu'un shell moderne ne tamponnerait rien n'importe où) et le second est incapable de chaîner correctement l'entrée / la sortie à travers un bloc. Il y a beaucoup d'ambiguïté (ou au moins de la place pour des différences d'opinions acceptables) sur ce à quoi le comportement correct devrait ressembler dans certains cas, mais je ne pense pas que quiconque défendra ce que fait actuellement le poisson comme étant optimal.

En général, vous avez des commandes externes (et des commandes intégrées qui sont effectivement traitées de la même manière, dans l'ensemble) qui sont faciles: une entrée, deux sorties, dont l'une peut être chaînée à la commande suivante, l'autre doit être redirigée vers un fichier ou le tty. Mais les blocs et les fonctions sont délicats car vous mappez essentiellement une entrée (car il ne peut y en avoir qu'une) à une séquence de commandes externes ou de commandes internes (ce qui finit par se développer).

Cela dit, je ne suis pas d'accord pour dire que le comportement actuel est mauvais. (cat) ne doit pas lire les données qui ont été envoyées dans la commande dans laquelle elle est exécutée:

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'

Vous envoyez l'entrée dans le bloc, que set consomme l'entrée, consomme une partie de l'entrée ou ignore complètement l'entrée, cat a raison de se connecter à /dev/tty pour l'entrée, qui est ensuite correctement transmise au shell pour la substitution dans la ligne de commande. En fait, il y a / il y avait (beaucoup) de bogues déposés contre ce dépôt se plaignant de cas où des "sous-shell" n'étaient _pas_ lus depuis le terminal lorsqu'ils étaient exécutés avec certains niveaux d'indirection. À mon humble avis, c'est bash qui est cassé ici, d' autant plus que bash prend en charge de vrais sous-shell et propose ici l'asynchronicité.

Le seul comportement cassé que je dirais provient de cas où des commandes externes sont lancées dans une fonction / un bloc et ne consomment pas complètement l'entrée:

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 Je suis très surpris mais cela fonctionne correctement:

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

Et cela est également correct:

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

Surtout en tenant compte du plan d'introduction un jour de vrais sous-coquillages dans les poissons avec une exécution asynchrone, je dirais que le comportement des poissons par rapport au cas original rapporté ici est correct. En fait, je suis enclin à clore complètement ce problème, à moins que quiconque ne s'y oppose et puisse faire un argument convaincant ici.

Bien que le rapport de bogue original soit à mon avis invalide, les problèmes soulevés par @waterhouse sont cat | meh signalé.

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>

Cela dit, je ne suis pas d'accord pour dire que le comportement actuel est mauvais. (cat) ne doit pas lire les données qui ont été envoyées dans la commande dans laquelle il est exécuté:

Je ne suis pas du tout d'accord là-dessus!

cat est correct de se connecter à / dev / tty pour l'entrée

C'est une question de modèle mental. Je dirais que cat se connecte à "le stdin actuel" pour l'entrée. Si la fonction ou le bloc n'est pas redirigé, c'est le tty. S'il est redirigé, c'est ça! Donc, se connecter à / dev / tty ici serait incorrect.

se plaindre des cas où les "sous-shell" ne lisaient pas depuis le terminal lorsqu'ils étaient exécutés avec certains niveaux d'indirection

Notez qu'il s'agissait de substitutions de commandes "globales". Par exemple, exécuter echo (fzf) sur la ligne de commande. Dans ce cas, il n'y a pas de stdin.

Donc, ce que je dirais fonctionnerait un peu comme ceci:

echo | echo (cat) # from tty

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

Il y a un problème connexe (# 1035) qui pose des questions sur stderr dans ce cas, et qui n'est pas redirigé. Ce qui était tout à fait un problème avec l'ancienne fonction math , car cela comportait une substitution de commande à l'intérieur, et vous ne pouviez donc pas rediriger cela.

C'est la partie stdin de celui-ci. Si une fonction fait un simple (cat) , est-il vraiment utile de toujours avoir cette lecture depuis le tty? Ou ne pourriez-vous pas simplement utiliser </dev/tty dans ce cas?

Pensées intéressantes.

Je suppose que cela revient à savoir si les parenthèses dénotent une simple substitution (c'est-à-dire "prétendre que le contenu des parenthèses était sur la ligne ci-dessus, les exécuter jusqu'à la fin, stocker le résultat dans une variable et remplacer la variable ici") ou si elles ' re (actuellement cassé) sous-shell. Je pensais que le consensus était que le poisson manquait de support de sous-coque approprié, mais l'intention a toujours été de corriger cela «à un moment donné».

Si c'est le premier, alors oui, je suis d'accord, le comportement actuel est cassé car si vous déplacez le contenu des parenthèses sur une ligne différente, il devrait certainement lire à partir de l'entrée redirigée vers le bloc.

Mais les sous-interpréteurs sont un concept beaucoup plus puissant que cela, et ils vous permettent de faire des choses qui ne sont pas possibles avec la substitution de commandes et de créer des scripts beaucoup plus réactifs et capables. Bien qu'il soit techniquement possible de connecter tout ce qui est entré dans le bloc au stdin d'une commande exécutée dans un sous-shell, je pense que ce serait incompatible avec le modèle mental là-bas .

si les parenthèses dénotent une simple substitution (c'est-à-dire "faire comme si le contenu des parenthèses était sur la ligne ci-dessus, les exécuter jusqu'à la fin, stocker le résultat dans une variable et remplacer la variable ici") ou si ce sont des sous-couches (actuellement cassées) .

Je ne pense pas que ces termes soient définis assez clairement pour être trop utiles ici.

Pour moi, cela revient à ce qui est plus naturel, plus typique et plus utile.

La lecture depuis le terminal est certainement utile, et parfois vous voulez lire depuis le terminal même si vous avez un autre stdin (par exemple fzf fait essentiellement cela exclusivement).

Mais je pense que la lecture depuis stdin est beaucoup plus typique, d'autant plus que les utilisations non interactives ne liront pas du tout depuis tty. Et comme la lecture depuis tty est toujours possible (via cette redirection </dev/tty ), il semble normal de laisser cela comme option secondaire.

Le fait qu'il n'y ait pas d'opposé de </dev/tty dans le modèle que je propose me fait reconsidérer ma position.

Je ne suis peut-être pas assez profond pour comprendre complètement la discussion. Mais j'ai besoin de résoudre quelque chose et je me demande si j'ai besoin d'un script bash pour le résoudre.

C'est fondamentalement une tâche très simple: je veux diriger stdout de (z) cat via pv vers un mysql cli (essentiellement pour restaurer une sauvegarde) et parce que je ne veux pas entrer la chaîne de connexion, je veux utiliser une fonction pour cela :

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

D'abord, j'étais sûr que cela fonctionnera car il est évident que stdin pour une commande est stdout de la commande sur le côté gauche, mais maintenant je suis confus. Ok mysqlenv n'est pas une commande c'est une fonction. Maintenant, je lis ici beaucoup de texte et beaucoup de "cela devrait fonctionner" mais rien ne fonctionne.

Ce que j'ai essayé:

  • cat -|mysql... pas de sortie; mysql n'obtient pas d'entrée; ctr + c existe mysql; le tuyau fonctionne en arrière-plan
  • mysql... <&0 pas de sortie; mysql n'obtient pas d'entrée; ctr + c existe mysql; le tuyau fonctionne en arrière-plan
  • set input (cat); mysql... pas de sortie; mysql n'obtient pas d'entrée; ctr + c existe tout; rien ne reste en arrière-plan
  • read -z|mysql... pas de sortie; mysql n'obtient pas d'entrée; ctr + c imprime ^c

Encore une fois mon invite de commande: zcat some_backup.sql.gz|pv -s (zsize some_backup.sql.gz)|mysqlenv . Il montre l'état du tube lorsqu'il est utilisé directement avec mysql (sans fonction de poisson entre les deux) - donc cela devrait fonctionner.

Alors s'il vous plaît, comment donner stdin de la fonction à stdin d'une commande à l'intérieur de la fonction?

Ne dites pas que je dois me reconnecter pour chaque ligne via while read... . Cela peut fonctionner mais ce n'est pas une solution car il est trop lent à travailler.

@tflori : Beaucoup plus simple. Laissez simplement la commande telle quelle, sans aucune redirection. Le problème n'est pas avec les commandes directement dans la fonction. Quelque chose comme

function foo
    cat
end

travaux. Le cat obtient stdin comme il se doit.

Ce qui ne l'est pas, c'est quand il s'agit d'une substitution de commande, c'est alors que vous rencontrez ce bogue.

@faho cela signifie que la fonction initiale devrait fonctionner? mais ce n'est pas le cas. peut-être que ma version est obsolète? J'utilise actuellement 2.7.1

peut-être que ma version est obsolète? J'utilise actuellement 2.7.1

@tflori : Oui, vous voudrez 3.0.2.

Cette page vous a été utile?
0 / 5 - 0 notes