Fish-shell: Pipe kann nicht gelesen werden, um zu funktionieren

Erstellt am 5. Juli 2012  ·  21Kommentare  ·  Quelle: fish-shell/fish-shell

Prüfung:

function testfun
    set input (cat)
    echo $input
end

echo testing123 | testfun

Dies sollte "testing123" ausgeben, erzeugt aber nichts.

Es funktioniert perfekt in Bash:

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

echo testing123 | testfun
bug

Hilfreichster Kommentar

Dieses Problem führt zusammen mit der Standard-Grep-Funktion für grep zu einigen Problemen. Wenn dieses Problem nicht in Kürze behoben wird, sollte der Standard-Grep-Alias ​​wahrscheinlich entfernt (oder durch ersetzt werden) vielleicht eine Abkürzung?), um das Auftreten zumindest zu minimieren.

Die Verwendung von cat wie @milieu vorschlägt, scheint das Problem für mich in Fish 2.3.1 nicht zu beheben (was ich gerade festgestellt habe, liegt etwas zurück, aber es ist die für Fedora 25 gepackte Version).

Alle 21 Kommentare

Sie können die Lesefunktion als Problemumgehung verwenden.

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

Ich habe gerade verstanden, dass es bei Fischen nicht funktioniert, weil das stdin auf "set" anstatt auf "cat" geleitet wird.

Dieses Problem geht tiefer als nur das "Setzen" einer bestimmten Arbeitsweise. Die Verrohrung in eine Funktion ist überhaupt vermasselt. Und an eine Funktion gesendete Terminal-E / A funktionieren zwar, sind aber immer noch etwas seltsam - sie scheinen die Eingabe zu puffern und alles auf einmal zu liefern. Beobachten Sie, was mit dieser Funktion passiert:

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

Außerdem verhält sich cat | meh | cat genauso wie cat | begin; cat; end .
Ich kann Ihnen weiter sagen, dass die "Katze", die sich über einen unterbrochenen Systemaufruf in cat | meh beschwert, die erste "Katze" ist. Das ist:

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

Da ist also das. Offensichtlich hat dies damit zu tun, wie Fisch ruft und wie er Rohre in sie baut. Weiß jemand davon?

Ok, ich finde das Laufen
pbpaste | begin; cat; end
wiederholt in einer frischen Fischschale, wobei die Zwischenablage "23 \ n" ist, wird manchmal nur 23 wieder ausgedruckt und manchmal wird die Schale blockiert, an welchem ​​Punkt Control-C nichts tun kann. Ich gehe davon aus, dass dies eine Art Rennbedingung sein muss. Oh Junge.

In der Zwischenzeit sieht es so aus, als würde das Signal SIGTTIN in ./mycat | begin; cat; end an den "mycat" gesendet:

     21    SIGTTIN      stop process         background read attempted from
                                             control terminal

Dann laut GNU libc-Handbuch: "Ein Prozess kann nicht vom Terminal des Benutzers lesen, während er als Hintergrundjob ausgeführt wird. Wenn ein Prozess in einem Hintergrundjob versucht, vom Terminal zu lesen, werden alle Prozesse im Job gesendet ein SIGTTIN-Signal. "

Es sieht also so aus, als würde der "Mycat" entweder im Hintergrund gestartet oder gestartet und dann in den Hintergrund gestellt, wenn er in eine Art Fischfunktion geleitet wird. Vielleicht hilft dieses Wissen.

Dies hinterhält anscheinend beide Seiten einer Pipe ... Wenn Sie jedoch den Befehl fg eingeben, wird der Prozess aus dem Hintergrund entfernt, sodass er wie vorgesehen funktioniert.

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

Ein bisschen nervig, dass ich ein pjson Wrapper-Skript in $PATH anstelle eines einfachen Alias ​​erstellen musste ... :(

Als Referenz ist dies auch der openSUSE-Fehler https://bugzilla.opensuse.org/show_bug.cgi?id=963548

Yay! Ich glaube, ich habe meine Problemumgehung gefunden! Dank des Kommentars von Fischpfeifen erläutert wird, habe ich

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

Dieses Problem führt zusammen mit der Standard-Grep-Funktion für grep zu einigen Problemen. Wenn dieses Problem nicht in Kürze behoben wird, sollte der Standard-Grep-Alias ​​wahrscheinlich entfernt (oder durch ersetzt werden) vielleicht eine Abkürzung?), um das Auftreten zumindest zu minimieren.

Die Verwendung von cat wie @milieu vorschlägt, scheint das Problem für mich in Fish 2.3.1 nicht zu beheben (was ich gerade festgestellt habe, liegt etwas zurück, aber es ist die für Fedora 25 gepackte Version).

Es scheint, dass es einen Unterschied zwischen der Ausführung innerhalb der Shell und der Befehlszeile gibt:

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

Vielleicht kann dies helfen, das Problem zu beheben?

@layus : Nein, das ist # 3805, ein Problem, bei dem Fische selbst nicht die Kontrolle über das Terminal erlangen können.

_Ich denke, einige der ursprünglichen Verhaltensweisen in dieser Hinsicht haben sich geändert, das unten ist in Bezug auf Fischmeister / 3.0_

Es gibt zwei grundlegende Probleme, bei denen Fisch hier falsch liegt: Das erste ist das Puffern von Funktionen / Blockausgaben (ich bin mir ziemlich sicher, dass eine moderne Shell nirgendwo etwas puffern

Im Allgemeinen verfügen Sie über externe Befehle (und integrierte Funktionen, die im Großen und Ganzen gleich behandelt werden), die einfach sind: eine Eingabe, zwei Ausgaben, von denen eine mit nachfolgenden Befehlen verkettet werden kann, von denen die andere umgeleitet werden muss eine Datei oder die tty. Blöcke und Funktionen sind jedoch schwierig, da Sie eine Eingabe (da es nur eine geben kann) grundsätzlich einer Folge von externen Befehlen oder integrierten Funktionen zuordnen (was letztendlich erweitert wird).

Trotzdem stimme ich nicht zu, dass das aktuelle Verhalten falsch ist. (cat) sollte keine Daten lesen, die in den Befehl geleitet wurden, in dem es ausgeführt wird :.

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'

Sie leiten Eingaben in den Block weiter, unabhängig davon, ob set die Eingabe verbraucht, einen Teil der Eingabe verbraucht oder die Eingabe vollständig ignoriert. Cat ist korrekt, um eine Verbindung zu /dev/tty für die Eingabe herzustellen, die dann korrekt an die übergeben wird Shell zur Ersetzung in die Kommandozeile. Tatsächlich wurden / wurden (viele) Fehler gegen dieses Repo eingereicht, die sich über Fälle beschwerten, in denen "Subshells" nicht vom Terminal gelesen wurden, wenn sie mit einigen Indirektionsebenen ausgeführt wurden. IMHO, es ist Bash, das hier kaputt ist, zumal Bash echte Subshells unterstützt und hier Asynchronität bietet.

Das einzige fehlerhafte Verhalten, das ich sagen würde, stammt aus Fällen, in denen externe Befehle in einer Funktion / einem Block gestartet werden und die Eingabe nicht vollständig verbrauchen:

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 Ich bin sehr überrascht, aber das funktioniert richtig:

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

Und das ist auch richtig:

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

Insbesondere wenn man den Plan berücksichtigt, eines Tages echte Unterschalen in Fische mit asynchroner Ausführung einzuführen, würde ich sagen, dass das Verhalten der Fische in Bezug auf den hier berichteten ursprünglichen Fall korrekt ist. Tatsächlich bin ich geneigt, dieses Problem vollständig zu schließen, es sei denn, jemand widerspricht und kann hier ein überzeugendes Argument vorbringen.

Während der ursprüngliche Fehlerbericht imho ungültig ist, sind die von @waterhouse aufgeworfenen Probleme cat | meh .

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>

Trotzdem stimme ich nicht zu, dass das aktuelle Verhalten falsch ist. (cat) sollte keine Daten lesen, die in den Befehl geleitet wurden, in dem es ausgeführt wird :.

Da bin ich nicht einverstanden!

cat ist korrekt, um eine Verbindung zu / dev / tty für die Eingabe herzustellen

Das ist eine Frage des mentalen Modells. Ich würde sagen, dass sich cat zur Eingabe mit "dem aktuellen Standard" verbindet. Wenn die Funktion oder der Block nicht umgeleitet wird, ist dies die tty. Wenn es umgeleitet wird, ist das das! Eine Verbindung zu / dev / tty hier wäre also falsch.

Beschwerde über Fälle, in denen "Subshells" nicht vom Terminal gelesen wurden, wenn sie mit einigen Indirektionsebenen ausgeführt wurden

Beachten Sie, dass es sich um "globale" Befehlsersetzungen handelte. Beispiel: Führen Sie echo (fzf) in der Befehlszeile aus. In diesem Fall gibt es kein stdin.

Was ich also sagen würde, würde so funktionieren:

echo | echo (cat) # from tty

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

Es gibt ein verwandtes Problem (# 1035), das in diesem Fall nach stderr fragt und das nicht umgeleitet wird. Das war ein ziemliches Problem mit der alten math -Funktion, da darin zufällig eine Befehlsersetzung enthalten war, die Sie nicht umleiten konnten.

Dies ist der Standardteil davon. Wenn eine Funktion nur (cat) , ist es dann wirklich nützlich, diese immer aus dem tty lesen zu lassen? Oder könnten Sie in diesem Fall nicht einfach </dev/tty ?

Interessante Gedanken.

Ich denke, es läuft darauf hinaus, ob die Klammern eine einfache Substitution bedeuten (dh "so tun, als ob der Inhalt der Klammern in der obigen Zeile steht, sie vollständig ausführen, das Ergebnis in einer Variablen speichern und die Variable hier ersetzen") oder ob sie ' re (derzeit defekte) Subshells. Ich dachte, der Konsens war, dass den Fischen die richtige Unterstützung für die Unterschale fehlt, aber die Absicht war immer, dies "irgendwann" zu beheben.

Wenn es das erstere ist, dann ist das aktuelle Verhalten fehlerhaft, denn wenn Sie den Inhalt der Klammern in eine andere Zeile verschieben, sollte er auf jeden Fall von der Eingabe gelesen werden, die in den Block umgeleitet wird.

Aber Subshells sind ein viel leistungsfähigeres Konzept als das, und sie ermöglichen es Ihnen, Dinge zu tun, die mit der Befehlssubstitution nicht möglich sind, und es mit dem mentalen Modell unvereinbar wäre.

ob die Klammern eine einfache Ersetzung bedeuten (dh "so tun, als ob der Inhalt der Klammern in der obigen Zeile steht, sie vollständig ausführen, das Ergebnis in einer Variablen speichern und die Variable hier ersetzen") oder ob es sich um (derzeit unterbrochene) Unterschalen handelt .

Ich denke nicht, dass diese Begriffe klar genug definiert sind, um hier zu nützlich zu sein.

Für mich kommt es darauf an, was natürlicher, typischer und nützlicher ist.

Das Lesen vom Terminal ist sicherlich nützlich, und manchmal möchten Sie vom Terminal lesen, obwohl Sie einen anderen Standard haben (z. B. fzf tut dies im Grunde ausschließlich).

Aber ich denke, dass das Lesen von stdin weitaus typischer ist, insbesondere wenn man bedenkt, dass nicht interaktive Verwendungen überhaupt nicht von tty lesen. Und da das Lesen von tty immer noch möglich ist (über diese </dev/tty -Umleitung), scheint es in Ordnung zu sein, dies als sekundäre Option zu belassen.

Die Tatsache, dass es in dem von mir vorgeschlagenen Modell kein Gegenteil von </dev/tty gibt, veranlasst mich, meine Position zu überdenken.

Ich bin vielleicht nicht tief genug in Muscheln, um die Diskussion vollständig zu verstehen. Aber ich muss etwas lösen und frage mich, ob ich ein Bash-Skript brauche, um es zu lösen.

Es ist im Grunde eine sehr einfache Aufgabe: Ich möchte stdout von (z) cat über pv zu einer MySQL-CLI leiten (im Grunde genommen, um ein Backup wiederherzustellen), und da ich die Verbindungszeichenfolge nicht eingeben möchte, möchte ich eine Funktion dafür verwenden ::

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

Zuerst war ich mir sicher, dass dies funktionieren wird, da es offensichtlich ist, dass stdin für einen Befehl stdout vom Befehl auf der linken Seite ist, aber jetzt bin ich verwirrt. Ok, mysqlenv ist kein Befehl, sondern eine Funktion. Jetzt lese ich hier viel Text und viel "das sollte funktionieren", aber nichts funktioniert.

Was ich versucht habe:

  • cat -|mysql... keine Ausgabe; MySQL erhält keine Eingabe; ctr + c existiert mysql; Rohr läuft im Hintergrund
  • mysql... <&0 keine Ausgabe; MySQL erhält keine Eingabe; ctr + c existiert mysql; Rohr läuft im Hintergrund
  • set input (cat); mysql... keine Ausgabe; MySQL erhält keine Eingabe; ctr + c existiert alle; nichts bleibt im Hintergrund
  • read -z|mysql... keine Ausgabe; MySQL erhält keine Eingabe; Strg + C druckt ^c

Wieder meine Eingabeaufforderung: zcat some_backup.sql.gz|pv -s (zsize some_backup.sql.gz)|mysqlenv . Es zeigt den Pipe-Status an, wenn es direkt mit MySQL verwendet wird (ohne dazwischen liegende Fischfunktion) - also sollte es funktionieren.

Wie kann man also stdin von der Funktion an stdin eines Befehls innerhalb der Funktion übergeben?

Sagen Sie nicht, dass ich für jede Leitung über while read... . Es mag funktionieren, ist aber keine Lösung, da es zu langsam ist, um damit zu arbeiten.

@tflori : Viel einfacher. Lassen Sie den Befehl einfach wie er ist, ohne Weiterleitungen. Das Problem liegt nicht bei den Befehlen direkt in der Funktion. Etwas wie

function foo
    cat
end

funktioniert. Das cat wird stdin wie es sollte.

Was nicht ist, wenn es sich um eine Befehlsersetzung handelt, dann tritt dieser Fehler auf.

@faho bedeutet das, dass die anfängliche Funktion funktionieren sollte? aber es tut nicht. Vielleicht ist meine Version veraltet? Ich verwende derzeit 2.7.1

Vielleicht ist meine Version veraltet? Ich verwende derzeit 2.7.1

@tflori : Ja, du wirst 3.0.2 wollen.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen