Fish-shell: Mehrzeilige Ausgabe kann nicht in einer Variablen gespeichert werden (unterstützt $()-Befehlsersetzungssyntax)

Erstellt am 20. Juni 2012  ·  110Kommentare  ·  Quelle: fish-shell/fish-shell

Es scheint keine Möglichkeit zu geben, mehrzeilige Ausgaben in einer Shell-Variablen zu erfassen. Jeder Versuch, dies zu tun, teilt jede Zeile in ein separates Array-Element auf. In bash würde ich meinen $() -Aufruf einfach in doppelte Anführungszeichen setzen, aber in fish können Sie keine Befehlssubstitution zitieren. Das ist ziemlich unglücklich, weil es bedeutet, dass ich keine mehrzeilige Ausgabe in einer Variablen erfassen und an einen separaten Befehl zurücksenden kann, ohne wirklich seltsame Tricks, wie das Ersetzen aller Zeilenumbrüche durch NUL beim Speichern und Zurücksetzen des Prozesses beim Ausgeben.

enhancement

Hilfreichster Kommentar

Persönlich bin ich sehr -1 bis $() Syntax.

Eines der Dinge, die mich zu Fish hingezogen haben, war, dass es all die verrückten verschiedenen Möglichkeiten vereinfacht, Dinge zu tun, die bash/zsh/etc haben.

Siehe das Design-Dokument , das Gesetz der Orthogonalität. Zitieren,

Die Shell-Sprache sollte eine kleine Menge orthogonaler Merkmale haben. In jeder Situation, in der zwei Merkmale verwandt, aber nicht identisch sind, sollte eines davon entfernt werden
...

  • Die vielen Posix-Zitatstile sind albern, besonders $''.

Ich denke, es wäre eine ernsthafte Verschlechterung der Prinzipien von fish, $() -Syntax hinzuzufügen.

Soweit ich weiß, war das Hinzufügen einer dritten Zitationssyntax ( "" vs. '' und ohne Anführungszeichen) eine große und schwierige Entscheidung. Wenn wir eine solche Änderung vornehmen, sollten wir zumindest alle Alternativen zuerst gründlich in Betracht ziehen.

Das Verständnis der Whitespace-Regeln im Moment ist ein großer Schmerzpunkt. Zur Zeit:

> count 'one two three'
1
> count one two three
3

Das bekomme ich. Anführungszeichen verhindern das Splitten. Cool.

> count (echo one two three)
1

Ok, und es sieht so aus, als würden Unterbefehle automatisch in Anführungszeichen gesetzt ...

> count one\ntwo\nthree
1

Und Zeilenumbrüche zählen nicht als Leerzeichen, wenn Sie Dinge aufteilen, das ist cool, denke ich

> count (echo one\ntwo\nthree)
3

Warte was?! Irgendetwas stinkt hier ernsthaft. Wenn sich (echo one two three) wie 'one two three' , dann sollte sich (echo one\ntwo\nthree) wie 'one\ntwo\nthree' .

Und natürlich besteht das große Scripting-Problem darin, dass die Daten von set nicht erhalten bleiben:

> set foo (echo one\ntwo)
> echo $foo
one two

Wir hatten Zeilenumbrüche und jetzt haben wir Leerzeichen.

Ich verstehe, warum das alles so funktioniert, aber es scheint definitiv nicht sehr fischartig zu sein. Der Benutzerfokus wird hier verletzt, Orthogonalität wird sicherlich nicht geholfen. Ich bin mir nicht sicher, was die Antwort ist, aber einige oder alle dieser Optionen gefallen mir besser als $() :

  1. Die Substitutionsausgabe sollte genau wie eine Zeichenfolge in Anführungszeichen behandelt werden. Wenn count "one\ntwo\nthree" 1 ist, dann sollte count (echo one\ntwo\nthree) 1 sein
  2. Alternativ können Sie die Substitutionsausgabe _ohne Anführungszeichen_ machen und sie in doppelte Anführungszeichen setzen. Dies ist eine ziemlich große semantische Änderung.
  3. Teilen Sie die Ausgabe standardmäßig nicht bei Zeilenumbrüchen. Fügen Sie einen dice -Befehl hinzu, der die Ausgabe in Zeilen in durch Leerzeichen getrennte Argumente in Anführungszeichen aufteilt. Dies ist analog dazu, wie wir psub anstelle von Prozesssubstitution haben: Fische sollten neue Befehle einer neuen Syntax vorziehen.
  4. Gib set ein neues Flag, das ihm sagt, wie Variablen gewürfelt werden.
    (Vorteile: Sie könnten Dinge auf Doppelpunkte aufteilen, wenn das Ihr Ding ist. Nachteil: hilft nicht bei der Verwendung count oder () .)
  5. Geben Sie echo ein neues Flag, das ihm mitteilt, wie Argumente getrennt werden sollen (behebt die Symptome, nicht das Problem.)

In Bezug auf Option 2 würde dies bedeuten, dass die Ersetzung von Befehlen ohne Anführungszeichen wie folgt funktioniert:

> count one two three
3
> count "one two three"
1
> count (echo one two three)
3
> count "(echo one two three)"
1

Was eine große Veränderung ist, aber zumindest konsequent ist.

Viele davon sind eher Workarounds als Lösungen. Und selbst wenn wir set und echo reparieren, indem wir weitere Flaggen hinzufügen, haben wir immer noch den count -Elefanten im Raum, der keine Flaggen jeglicher Art aufnehmen kann: Wenn wir Fügen Sie set Flags hinzu, die ihm mitteilen, wie Arrays geteilt werden sollen, dann erscheint es ziemlich albern, dass count niemals die gleiche Funktionalität haben kann.

Ich bin am meisten für 1. oder 2. oben, aber so oder so bin ich dagegen, die $() -Syntax hinzuzufügen. Noch ein Design-Doc-Zitat:

Die meisten Kompromisse zwischen Leistung und Benutzerfreundlichkeit können durch sorgfältiges Design vermieden werden.

Die meisten meiner oben genannten Ideen sind noch unausgereift, aber ich denke, dass ein sorgfältigeres Entwerfen der Textaufteilungsregeln eine weitaus bessere Option ist, als mehr Syntax und Flags zusätzlich zu den vorhandenen hinzuzufügen, um alle Lücken zu schließen. Diese Art von Aktivität hat Bash dahin gebracht, wo es heute ist.

Alle 110 Kommentare

Und fürs Protokoll, eine solche Ersetzung macht es unmöglich, den Status des ursprünglichen Befehls* (z. B. in Ausgabe #158) zu sehen, da Fish keinen Ersatz für PIPESTATUS von bash bietet.

*ohne wirklich dumme Dinge wie das vorübergehende Weiterleiten der Ausgabe in eine Datei

Als separate Idee, wenn es ein Flag für echo gäbe, das jedes Argument in einer separaten Zeile anstelle einer Leerzeichentrennung ausgeben würde, würde dies zumindest eine Problemumgehung bieten. Und ja, das ist etwas, das Shellscripting sein kann. Aber es sollte wirklich nicht nötig sein.

Das Fehlen einer Möglichkeit, eine Befehlsersetzung zu zitieren, ist gerade an einer anderen Stelle aufgetaucht: die Unfähigkeit, die Befehlsersetzung mit test -n zu verwenden. Wenn die Befehlsersetzung zu keiner Ausgabe führt, führt sie auch zu überhaupt keinen Argumenten, und test -n ohne nachfolgende Argumente gibt tatsächlich wahr zurück.

Für mich sieht es ganz natürlich aus: keine Ausgabe - keine Argumente. Eine leere Ausgabezeile - ein leeres Argument.
Was für mich seltsam ist, ist, warum 'test -n' überhaupt 0 zurückgibt? Naiv würde ich annehmen, dass es 1 zurückgibt.
Ich würde vorschlagen, den Rückgabewert für diesen Fall zu ändern, aber das System „test“ und der Bash-interne „test“ geben in diesem Fall auch 0 zurück.

Bezüglich der mehrzeiligen Ausgabe stimme ich dir voll und ganz zu. Es wäre sehr schön, die Möglichkeit zu haben, Befehle in doppelten Anführungszeichen zu ersetzen, so etwas wie "$()", weil das Ersetzen von "()" gefährlich ist.

Als Alternative zu $() (was ich übrigens immer noch möchte, damit ich einfacher Befehlsersetzungen mit umgebenden Variablen usw. kombinieren kann) könnte es nett sein, einfach eine triviale Möglichkeit zu haben, ein Multi-Element zu nehmen -Variable und Erstellen eines Arguments mit allen Elementen, die durch einen Zeilenumbruch verbunden sind. Dies würde den Vorgang des Speicherns einer Befehlsersetzung in einer Variablen effektiv umkehren, mit der einzigen Ausnahme, dass dies einen abschließenden Zeilenumbruch erzwingt, wenn die Befehlsersetzung möglicherweise keinen hatte. Im Moment kann ich (for elem in $var; echo $elem; end) schreiben, aber das ist überall ziemlich umständlich.

Kürzere Wege, dasselbe zu tun:
(printf '%sn' $var)
oder
(echo $varn)?
Aber Fisch teilt die Ersetzung wieder in Argumente durch Zeilenumbrüche auf. Wie benutzt man es?

Ich nehme an, Sie meinen printf , und das würde funktionieren, aber es muss einen separaten Prozess erzeugen, der langsam ist.

Was (echo $var\n) betrifft, so wird nach jedem Zeilenumbruch ein Leerzeichen gesetzt, was falsch ist, sowie zwei Zeilenumbrüche am Ende.

Die Verwendung davon ist für die Leitung zu einem anderen Prozess. Offensichtlich würde das Zurückspeichern in eine Variable es nur wieder aufteilen, aber ich kann (for line in $var; echo $line; end | grep foo) sagen

Sie haben Recht.
echo können Sie verwenden, wenn Sie sich nicht um Extraspace kümmern (wie im grep-Beispiel). Und printf, wenn Ihnen die Leistung egal ist.

Dies gibt 1 aus:

count (printf "%s %s %s" first second third)

Dies gibt 3 aus:

count (printf "%s\n%s\n%s" first second third)

Ich verstehe nicht, warum Zeilenumbrüche aus einer Ersetzung zu Arrays führen, während Leerzeichen aus einer Ersetzung dies nicht tun. Ich frage mich, wie schlecht es wäre, Leerzeichen hier einheitlich zu behandeln.

Ich denke, dass es ein vollkommen konsistentes Verhalten ist (wenn man bedenkt, dass Fisch Leerzeichen unterscheidet). Aus meiner Erfahrung erschwert das automatische Erweitern von durch Leerzeichen getrennten Zeichenfolgen zu Arrays den Umgang mit Zeichenfolgen, die Leerzeichen enthalten, und führt zu ziemlich umfangreichen Unterzitaten.

Andererseits: Zusätzliches Splitten kann durch zusätzlichen Befehl implementiert werden. Aber wenn Fische alle Leerzeichen teilen, wird es schwierig sein, sie bei Bedarf wieder zusammenzukleben.

Mein Vorschlag ging eigentlich in die andere Richtung: Zeilenumbrüche wie Leerzeichen behandeln (damit sie nicht geteilt werden). So

count (printf "%s\n%s\n%s" first second third)

würde 1 ausgeben. Andererseits bräuchten wir dann eine Möglichkeit, einen String zu teilen.

Ich denke auch, dass kballards Vorschlag von Ersetzungen in Anführungszeichen über zB "$(echo hello)" sehr gut ist und dies ordentlich ansprechen würde.

Bei "$(echo hallo)" stimme ich vollkommen zu.
Ich denke auch manchmal, dass das Ersetzen von () durch $() im normalen Modus eine gute Sache ist
Weise, da in diesem Fall kein Escape () erforderlich ist.

Persönlich bin ich sehr -1 bis $() Syntax.

Eines der Dinge, die mich zu Fish hingezogen haben, war, dass es all die verrückten verschiedenen Möglichkeiten vereinfacht, Dinge zu tun, die bash/zsh/etc haben.

Siehe das Design-Dokument , das Gesetz der Orthogonalität. Zitieren,

Die Shell-Sprache sollte eine kleine Menge orthogonaler Merkmale haben. In jeder Situation, in der zwei Merkmale verwandt, aber nicht identisch sind, sollte eines davon entfernt werden
...

  • Die vielen Posix-Zitatstile sind albern, besonders $''.

Ich denke, es wäre eine ernsthafte Verschlechterung der Prinzipien von fish, $() -Syntax hinzuzufügen.

Soweit ich weiß, war das Hinzufügen einer dritten Zitationssyntax ( "" vs. '' und ohne Anführungszeichen) eine große und schwierige Entscheidung. Wenn wir eine solche Änderung vornehmen, sollten wir zumindest alle Alternativen zuerst gründlich in Betracht ziehen.

Das Verständnis der Whitespace-Regeln im Moment ist ein großer Schmerzpunkt. Zur Zeit:

> count 'one two three'
1
> count one two three
3

Das bekomme ich. Anführungszeichen verhindern das Splitten. Cool.

> count (echo one two three)
1

Ok, und es sieht so aus, als würden Unterbefehle automatisch in Anführungszeichen gesetzt ...

> count one\ntwo\nthree
1

Und Zeilenumbrüche zählen nicht als Leerzeichen, wenn Sie Dinge aufteilen, das ist cool, denke ich

> count (echo one\ntwo\nthree)
3

Warte was?! Irgendetwas stinkt hier ernsthaft. Wenn sich (echo one two three) wie 'one two three' , dann sollte sich (echo one\ntwo\nthree) wie 'one\ntwo\nthree' .

Und natürlich besteht das große Scripting-Problem darin, dass die Daten von set nicht erhalten bleiben:

> set foo (echo one\ntwo)
> echo $foo
one two

Wir hatten Zeilenumbrüche und jetzt haben wir Leerzeichen.

Ich verstehe, warum das alles so funktioniert, aber es scheint definitiv nicht sehr fischartig zu sein. Der Benutzerfokus wird hier verletzt, Orthogonalität wird sicherlich nicht geholfen. Ich bin mir nicht sicher, was die Antwort ist, aber einige oder alle dieser Optionen gefallen mir besser als $() :

  1. Die Substitutionsausgabe sollte genau wie eine Zeichenfolge in Anführungszeichen behandelt werden. Wenn count "one\ntwo\nthree" 1 ist, dann sollte count (echo one\ntwo\nthree) 1 sein
  2. Alternativ können Sie die Substitutionsausgabe _ohne Anführungszeichen_ machen und sie in doppelte Anführungszeichen setzen. Dies ist eine ziemlich große semantische Änderung.
  3. Teilen Sie die Ausgabe standardmäßig nicht bei Zeilenumbrüchen. Fügen Sie einen dice -Befehl hinzu, der die Ausgabe in Zeilen in durch Leerzeichen getrennte Argumente in Anführungszeichen aufteilt. Dies ist analog dazu, wie wir psub anstelle von Prozesssubstitution haben: Fische sollten neue Befehle einer neuen Syntax vorziehen.
  4. Gib set ein neues Flag, das ihm sagt, wie Variablen gewürfelt werden.
    (Vorteile: Sie könnten Dinge auf Doppelpunkte aufteilen, wenn das Ihr Ding ist. Nachteil: hilft nicht bei der Verwendung count oder () .)
  5. Geben Sie echo ein neues Flag, das ihm mitteilt, wie Argumente getrennt werden sollen (behebt die Symptome, nicht das Problem.)

In Bezug auf Option 2 würde dies bedeuten, dass die Ersetzung von Befehlen ohne Anführungszeichen wie folgt funktioniert:

> count one two three
3
> count "one two three"
1
> count (echo one two three)
3
> count "(echo one two three)"
1

Was eine große Veränderung ist, aber zumindest konsequent ist.

Viele davon sind eher Workarounds als Lösungen. Und selbst wenn wir set und echo reparieren, indem wir weitere Flaggen hinzufügen, haben wir immer noch den count -Elefanten im Raum, der keine Flaggen jeglicher Art aufnehmen kann: Wenn wir Fügen Sie set Flags hinzu, die ihm mitteilen, wie Arrays geteilt werden sollen, dann erscheint es ziemlich albern, dass count niemals die gleiche Funktionalität haben kann.

Ich bin am meisten für 1. oder 2. oben, aber so oder so bin ich dagegen, die $() -Syntax hinzuzufügen. Noch ein Design-Doc-Zitat:

Die meisten Kompromisse zwischen Leistung und Benutzerfreundlichkeit können durch sorgfältiges Design vermieden werden.

Die meisten meiner oben genannten Ideen sind noch unausgereift, aber ich denke, dass ein sorgfältigeres Entwerfen der Textaufteilungsregeln eine weitaus bessere Option ist, als mehr Syntax und Flags zusätzlich zu den vorhandenen hinzuzufügen, um alle Lücken zu schließen. Diese Art von Aktivität hat Bash dahin gebracht, wo es heute ist.

Vielen Dank für die nachdenklichen Kommentare Soares.

Ich denke, es könnte einen Punkt der Verwirrung geben. Der $()-Vorschlag besteht darin, eine Möglichkeit hinzuzufügen, Befehlsersetzungen in doppelten Anführungszeichen durchzuführen. Es muss nicht außerhalb von ihnen funktionieren:

> count (echo 1\n2\n3)
3
> count "$(echo 1\n2\n3\n)"
1
> count $(echo 1\n2\n3\n)
syntax error

Auf diese Weise denke ich, dass es Ihrem Vorschlag 2 oben ähnlich ist. Das Dollarzeichen hat den Vorteil, dass keine Klammern in doppelten Anführungszeichen gesetzt werden müssen (was irritierend wäre) und dass es nicht mit einer bestehenden gültigen Syntax kollidiert.

Dies war ein Element von kballards „Fish Scripting Wishlist“ vom 21. Juni. Um Kevin zu zitieren:

  1. Fish braucht eine Möglichkeit, Befehle innerhalb einer Zeichenfolge in doppelten Anführungszeichen zu ersetzen. Die einfachste Lösung besteht wahrscheinlich darin, $() in doppelten Anführungszeichen zu unterstützen. Dafür gibt es zwei Gründe: Erstens wird eine Befehlsersetzung, die keine Ausgabe ergibt, vollständig aus der Argumentliste des umgebenden Befehls entfernt. Dies hat extrem negative Auswirkungen, wenn es mit zB test -n (some command) verwendet wird. Zweitens würde dies ermöglichen, die Ausgabe mit ihren Zeilenumbrüchen in einer Variablen zu speichern, anstatt die Ausgabe in mehrere Elemente aufzuteilen. Und als Bonus würde es es viel einfacher machen, die Ausgabe der Befehlsersetzung mit anderem Text im selben Argument zu kombinieren, zB "$somevar"(irgendein Befehl)"$anothervar".

"$()" würde also mehrere Probleme lösen, weshalb es interessant ist.

Ich denke, wir möchten auch das Aufteilen bei Zeilenumbrüchen vermeiden, wie Sie oben in 3 vorgeschlagen haben. Der Hauptblocker dort ist die schiere Menge an Arbeit, die erforderlich ist, um den gesamten vorhandenen Fischcode zu überprüfen und den neuen 'Würfel'-Befehl hinzuzufügen.

Cool. Ich bin viel weniger gegen diese Syntax, wenn sie nur in doppelten Anführungszeichen steht.

(Hinweis: In Fish 1.x wurden Variablen in doppelten Anführungszeichen zum _ersten_ Argument in diesem Variablenarray erweitert und es gab keine Möglichkeit, die anderen Argumente zu schneiden/zu erhalten. Dies war ein Zugeständnis an Axel Liljencrantz IIRC, der keine doppelten Anführungszeichen wollte an erster Stelle und bestand darauf, etwas zu haben, das doppelte Anführungszeichen unterscheidet, was ich für dumm hielt. Ich war kurz davor, über die magischen Dinge zu schimpfen, die doppelte Anführungszeichen bewirken, bevor mir klar wurde, dass doppelte Anführungszeichen in fish2 ziemlich vernünftig sind. 0. Gute Arbeit daran!)

Es kommt mir immer noch komisch vor, $() statt () zu verwenden. Ich verstehe, wie mühsam es wäre, allen Klammern in doppelten Anführungszeichen zu entkommen, aber ist das nicht der Sinn von einfachen Anführungszeichen anstelle von doppelten Anführungszeichen?

Außerdem bin ich vorsichtig, $() in doppelten Anführungszeichen mit POSIX $() zu verschmelzen; es könnte für Neulinge verwirrend sein. Ich würde die Syntax #() oder {} bevorzugen, um uns etwas vom Dollarzeichen zu distanzieren, das bisher _nur_ Variablenerweiterung bedeutet. (Die Tatsache, dass es in () nicht verwendet wird, stellt so etwas wie einen Präzedenzfall dafür dar, das Dollarzeichen nicht wiederzuverwenden.)

Ich denke auch, dass das Erweitern einer leeren Variablen '' ergeben sollte, denn selbst wenn wir eine Erweiterung in doppelten Anführungszeichen haben, haben Sie immer noch die alte

> set var
> test -n $var

Problem, das für Neuankömmlinge ein ernsthaftes Problem darstellt.

Was ist POSIX $() ? Bash unterstützt seit geraumer Zeit $() in doppelten Anführungszeichen, um genau das zu tun, was wir hier vorschlagen, und niemand hat damit ein Problem. Das Hinzufügen einer weiteren Syntax für die Prozesserweiterung scheint eine wirklich sehr schlechte Idee zu sein. Sie sagen, Dollarzeichen bedeutet bisher nur variable Expansion. Das ist in Ordnung, aber ich sehe kein Problem darin, dies auf eine Erweiterung im Allgemeinen zu erweitern, wobei die beiden unterstützten Erweiterungstypen entweder Variablen oder Unterprozesse sind.

Das Erweitern einer leeren Variablen sollte niemals '' ergeben, es sei denn, diese Variable ist in doppelte Anführungszeichen gesetzt. Das zu ändern würde im Grunde eine Anforderung einführen, eval zu verwenden, wenn Sie ein Argument bedingt hinzufügen möchten, was eine wirklich schlechte Idee ist. Ich würde mir wegen test -n $var keine allzu großen Sorgen machen; Neulinge, die nicht verstehen, was sie tun, haben schlimmere Probleme, als einem Argument aufgrund einer leeren Variablenexpansion auszuweichen.

Bashs $() meinte ich, als ich posix sagte (ist das posix?), da ich bash nicht hervorheben wollte. Bash unterstützt die $() -Syntax überall, nicht nur in doppelten Anführungszeichen: Wenn fish sie unterstützt, aber nur unter besonderen Umständen, erscheint das etwas seltsam.

Zumal fish die Befehlssubstitution beibehält, aber die Syntax von $() in () ändert. Der Wechsel von $() zu () für die Befehlsersetzung impliziert, dass Fish versucht, sich von dem `` $() ${} <() Höllenloch zu distanzieren, das die bash/zsh-Ersetzung ist; Es scheint seltsam, sich umzudrehen und die $() -Syntax wieder in doppelte Anführungszeichen zu setzen.

Aus diesem Grund bin ich vorläufig dafür, in den sauren Apfel zu beißen und () in doppelten Anführungszeichen zuzulassen, obwohl das ziemlich rückwärtskompatibel ist.

Ich würde es für äußerst überraschend und absolut schrecklich halten, () in doppelten Anführungszeichen als Ersatz zu interpretieren. Derzeit erfordert die _nur_ Ersetzung, die innerhalb von doppelten Anführungszeichen auftritt (Hinweis: Escaping ist keine Ersetzung), ein $ -Zeichen. Es gibt keinen guten Grund, separate Möglichkeiten zum Aufrufen der Ersetzung innerhalb von Zeichenfolgen in doppelten Anführungszeichen einzuführen.

Ja, ich gebe zu, dass dies aus heutiger Sicht die beste Vorgehensweise sein könnte. Das Gegenteil zu Ihrer Aussage ist jedoch Folgendes:

Denken Sie daran, dass die _einzige_ Ersetzung, die außerhalb von Anführungszeichen auftritt, aus den Zeichen $ und () besteht. Es gibt keinen guten Grund, eine neue Syntax hinzuzufügen, nur um eine der vorhandenen Substitutionsmethoden einzuschränken.

$ und () außerhalb von Anführungszeichen zu ersetzen, während $ und $() innerhalb von Anführungszeichen ersetzt werden, ist chaotisch und dichotom und genau das, was fischen ( wie bash/zsh/etc) soll vermieden werden. Es steht in direktem Gegensatz zu einigen Teilen des Designdokuments und wirft die Frage auf: "Wenn ich $() in einem String verwende, warum verwende ich es nicht außerhalb eines Strings?".

Es ist eine beschissene Situation. () wurde für die Befehlsersetzung gewählt, als fish keine Strings in doppelten Anführungszeichen hatte. $var wird bereits in Zeichenfolgen in doppelten Anführungszeichen unterstützt, also können wir es nicht im Ruby-Stil machen und sagen, dass #{} die doppelten Anführungszeichen aufhebt, sodass Sie Variablen wie verwenden müssen #{$var} und Befehlsersetzungen wie #{(command args)} .

Wenn Sie wirklich rein sein wollen, könnten Sie $() auch außerhalb von Zeichenfolgen in doppelten Anführungszeichen einführen und den bloßen () -Stil verwerfen. Auf diese Weise wird "die einzige auftretende Ersetzung erfordert $ " wahr, unabhängig vom Zitieren.

An dieser Stelle können Sie natürlich sagen: "Was ist mit {} ?" Obwohl dies streng genommen keine Substitution ist, könnten Sie dann argumentieren, dass wir dies auch in ${} ändern sollten. Und eigentlich hätte ich nichts dagegen einzuwenden. Die Tatsache, dass fish {} speziell interpretiert, selbst wenn nur ein Zweig darin ist, ist sehr ärgerlich, wenn man versucht, mit Git zu arbeiten. Außerdem könnten Sie dann problemlos ${} innerhalb von Zeichenfolgen in doppelten Anführungszeichen zulassen.

Aus praktischer Sicht halte ich es jedoch für unnötig, diese Änderungen hier vorzunehmen. Ich würde lieber einfach $() innerhalb von doppelten Anführungszeichen einfügen und damit fertig sein.

Ja, wenn die Sprache von Anfang an entworfen wurde, würde ich empfehlen, die bloße Version zu streichen. Wenn $() zu Zeichenfolgen in doppelten Anführungszeichen hinzugefügt wird, dann denke ich definitiv, dass es einen langfristigen Plan geben sollte, um die Konsistenz wiederherzustellen, was meiner Meinung nach wichtig ist, um in seiner Rolle als Bastion gegen Shell-Scripting-Wahnsinn zu fischen.

Nur eine Anmerkung: Wenn Sie lange Zeichenfolgen in doppelten Anführungszeichen haben, können Sie dies bereits tun

> "have command substitution "(in double-quoted strings)" like this."

Es ist ein Zeichen mehr und erfordert keine zusätzliche Syntax.

Es ist wirklich nur der Grenzfall von "$(some expression)" , wo die neue Syntax nützlich ist, und ich würde es vorziehen, wenn das durch eine vernünftige Handhabung von Zeilenumbrüchen und eine nette dice -Funktion behoben würde. Wenn wir diese bekommen, bin ich mir nicht sicher, ob wir überhaupt die $() -Syntax brauchen.

Nein, das kannst du nicht.

> echo "test"(echo one\ntwo)"ing"
testoneing testtwoing

Das gewünschte Verhalten ist

>echo "test$(echo one\ntwo)ing"
testone
twoing

Richtig. Verzeihung. Ich wollte damit sagen, dass Sie das tun können, wenn wir dafür sorgen, dass Variablen standardmäßig nicht in Zeilenumbrüchen aufgeteilt werden, und einen dice -Befehl hinzufügen (wie oben besprochen), dann funktioniert die vorhandene Syntax.

Soares, aus Neugier, woher hast du den Namen für den Befehl dice ? Wir sind daran interessiert, etwas ausgefeiltere String-Manipulationen hinzuzufügen, und ich würde lieber eine bestehende Syntax übernehmen, als eine neue zu erfinden.

Aus irgendeinem Grund dachte ich, der vorhandene split -Befehl hieße "slice". Angesichts der Tatsache, dass split Dinge nach Länge und nicht nach Inhalt aufteilt, dachte ich, ein Befehl, der Dinge nach Inhalt und nicht nach Länge aufteilt, könnte dice heißen (wie in 'Slice and Dice').

Da ich jetzt an split dachte, ist slice wahrscheinlich ein viel besserer Name für den Befehl, den wir besprechen.

Soares, danke für diese lange argumentierte Antwort. jetzt stimme ich dir zu (:

@Soares , sehr gut geschrieben und gute Argumente, ich stimme hier der Konsistenz zu, ich sehe keinen Sinn darin, eine Syntax in doppelten Anführungszeichen und eine außerhalb von doppelten Anführungszeichen zu haben.
Ich würde auch nicht $() für jede Befehlsersetzung (außerhalb von "" ) eingeben wollen, die ich täglich mache, aber ich sehe das Problem, die Bedeutung von () innerhalb von "" zu ändern
@kballard , das Problem mit {} wird durch #354 gelöst (entfernen).

Zusammenfassend, entweder nur $var & $() oder $var & () , würde ich die zweite Option bevorzugen, obwohl sie schrecklich kaputt geht.

Wie wäre es mit einer Option 2a: Die Substitutionsausgabe sollte wie eine Zeichenfolge ohne Anführungszeichen behandelt werden, und anstelle von dice oder slice und list kann das Teilen und Verbinden mit dem IFS erfolgen

> count a b
2
> count (echo a b)
2  # As we usually want
> set -l IFS ''; count (echo a b)
1
> set -l IFS '\n'; count (echo a b)
1

Ich hasse IFS. Meiner Meinung nach ist es einer der schlimmsten Teile des Bash-Skriptings.

Vereinbart auf dem IFS-Hass.

Ich kann auch den schleichenden Verdacht nicht unterdrücken, dass wir eine Neuimplementierung vornehmen werden
lispeln.

Aber im Ernst, die Array-Syntax verspricht ziemlich leistungsfähig zu sein, wenn wir können
mach es richtig.

Am Mittwoch, den 12. Dezember 2012 um 23:08 Uhr schrieb Kevin Ballard [email protected] :

Ich hasse IFS. Meiner Meinung nach ist es einer der schlimmsten Teile des Bash-Skriptings.


Antworten Sie direkt auf diese E-Mail oder zeigen Sie sie auf GitHubhttps://github.com/fish-shell/fish-shell/issues/159#issuecomment -11324611 an.

Ich habe gerade wegen der list -Diskussion darüber nachgelesen.

Eine Sache, die in dieser Diskussion fehlt, ist, warum das Splitting-Verhalten derzeit so ist, wie es ist:

me<strong i="8">@mypc</strong> ~/test> ls -1
another file
file 1
third file
me<strong i="9">@mypc</strong> ~/test> count (ls)
3

Wenn Vorschlag 1 umgesetzt würde:

me<strong i="13">@mypc</strong> ~/test> count (ls)
1

wenn Vorschlag 2 oder 3 umgesetzt wurden:

me<strong i="17">@mypc</strong> ~/test> count (ls)
6

Zeilenumbruch und Leerzeichen/Tabulator verhalten sich im aktuellen Unix als Trennzeichen der Ebenen 1 und 2. Das kommt (glaube ich) von menschlichem Text, wo Zeilenumbruch und Leerzeichen in gewissem Sinne dasselbe tun. In der Befehlsausgabe bedeutet beides nicht schon etwas und manchmal braucht man Leerzeichen, also war die logische Wahl, das Trennzeichen der Ebene 1 zu verwenden. Bei der Eingabe von Befehlen wird der Zeilenumbruch bereits als Trennzeichen zwischen verschiedenen Befehlen verwendet, daher wurde das Trennzeichen der Ebene 2 verwendet. Auf diese Weise wurde die gesamte Befehlszeilen-Unix-Software geschrieben, und Fisch folgt einfach, indem er das in der Befehlsausgabe verwendete Trennzeichen der Ebene 1 in das Trennzeichen der Ebene 2 konvertiert, das count oder ein beliebiges Befehlszeilenargument verwendet. Es gibt Millionen von Programmen, die eine Ausgabe im Format einer Zeile pro Element liefern.

Dies ermöglicht zB die Verwendung von Leerzeichen in Dateinamen. Natürlich bricht es ab, wenn es einen Zeilenumbruch in einem Dateinamen gibt oder irgendwo, wo Sie nicht wollen, dass es die Ausgabe aufteilt, und das allgemeine Design hat sich als scheiße herausgestellt, aber _Sie können es nicht ändern, ohne die gesamte jemals geschriebene Unix-Befehlszeilensoftware zu ändern! _ Dies würde Fish für die Interaktion mit nicht fischbewussten Befehlen zum Kotzen bringen -- und daher als Shell.

Also -1000 beim Ändern der Standardaufteilung von Leerzeichen. Allerdings bin ich für so etwas wie einen list Befehl, und ich hasse auch $IFS .

Ich denke wirklich, Konsistenz ist König und Fisch sollte eines der folgenden tun:

  1. Erlauben Sie alle Ersetzungen _und_ Escapezeichen _und_ Erweiterungen in doppelten Anführungszeichen mit genau der gleichen Syntax. Anführungszeichen begrenzen dann einfach Argumente, die Leerzeichen enthalten, ohne die Semantik zu ändern. Wenn Sie eine wörtliche Zeichenfolge wünschen, maskieren Sie entweder die Sonderzeichen oder verwenden Sie einfache Anführungszeichen.
  2. Entfernen Sie die Unterstützung für doppelte Anführungszeichen oder lassen Sie sie sich wie einfache Anführungszeichen verhalten. Anführungszeichen sind dann "String-Literale".

Für Option 1 bin ich entschieden dagegen, die Syntax für die Befehlsersetzung zu ändern, und für Option 2 brauchen wir eine andere Möglichkeit, um zu sagen: "Diese Befehlsersetzung als eine einzelne Zeichenfolge behandeln, unabhängig von Zeilenumbrüchen". Ich bevorzuge die erste Option.

Oder 3. eine Syntax ohne Anführungszeichen wie #{} Ruby haben, aber ich würde es vorziehen, wenn es nur {} wäre. Ich bin kein Fan dieser Option, da "{(ls)}" etwas umständlich ist und bedeutet, dass doppelte Anführungszeichen fast dasselbe sind wie einfache Anführungszeichen.

Die erste Option dafür klingt vollkommen gültig und würde wahrscheinlich viele Probleme lösen. Es wirkt orthogonaler.

Ich weiß nicht, wie es anderen hier geht, aber ich verwende selten wörtliche () -Zeichen als Teil einer Zeichenfolge. Wenn andere Leute so denken, wäre es vielleicht nicht so wichtig, sie maskieren zu müssen, um eine Ersetzung zu verhindern.

Gibt es alternativ zu den folgenden Nebenproblemen - verwenden ( , um eine Ersetzung zu beginnen. ( , um Klammern normal zu verwenden (oder umgekehrt), wenn auf eine ein Leerzeichen folgt.

@kevna : Das gefällt mir. Ich war mir nicht sicher, ob ich es vorschlagen sollte, aber jetzt denke ich, dass es eine gute Idee ist. Wenn ich trotzdem ( eingebe, ist das Teil des Codes (z. B. Perl-Einzeiler) oder eines regulären Ausdrucks, und normalerweise schreibe ich Code in einfache Anführungszeichen, um zu vermeiden, dass Variablen in das Ergebnis geschrieben werden.

Andererseits verwendet ein Code (hauptsächlich im Zusammenhang mit Vervollständigungen) ( in doppelten Anführungszeichen). Aber ich denke, dass Vervollständigungen relativ einfach zu beheben sind.

Ich weiß nicht, wie viel davon tatsächlich bearbeitet wurde, aber ich dachte nur an eine bessere Alternative: Warum nicht einfach (( und )) dazu bringen, die Ausgabe des Befehls buchstäblich als ein Argument einzufügen? Es sieht ähnlich aus wie die Bash-Syntax für eine Bedingung, aber in Fällen, in denen Bash-Bedingungen verwendet würden, würde es sowieso einen Fehler wegen ungültiger Syntax auslösen. (nur eine Subshell haben, da der Befehl nicht funktioniert)

Das ist nicht mehrzeilig.

Ich habe das nur zum Spaß gemacht. Die echte Lösung wäre natürlich schön, da es derzeit keine wirkliche Möglichkeit gibt, dies zu tun. Dies ist ein wirklich benötigtes Feature, das Problem ist, dass niemand über seine Syntax entscheiden kann. Offensichtlich ist pipeset keine vorgeschlagene Syntax, sondern nur eine Funktion, da ich keine neue Syntax aus der Fischschale selbst erstellen kann.

function pipeset --no-scope-shadowing
    set -l _options
    set -l _variables
    for _item in $argv
        switch $_item
            case '-*'
                set _options $_options $_item
            case '*'
                set _variables $_variables  $_item
        end
    end
    for _variable in $_variables
        set $_variable ""
    end
    while read _line
        for _variable in $_variables
            set $_options $_variable $$_variable$_line\n
        end
    end
    return 0
end

Beispiel:

~ $ perl --version | pipeset perl_version
~ $ echo $perl_version

This is perl 5, version 14, subversion 4 (v5.14.4) built for cygwin-thread-multi
(with 7 registered patches, see perl -V for more detail)

Copyright 1987-2013, Larry Wall

Perl may be copied only under the terms of either the Artistic License or the
GNU General Public License, which may be found in the Perl 5 source kit.

Complete documentation for Perl, including FAQ lists, should be found on
this system using "man perl" or "perldoc perl".  If you have access to the
Internet, point your browser at http://www.perl.org/, the Perl Home Page.


~ $

@xfix Das ist eine nette Lösung. Es wäre schön, wenn man einfach zu set könnte.

> perl --version | set perl_version
> echo $perl_version

Die Explode/Implode-Funktionen von PHP lassen sich leicht an eine Shell-Syntax anpassen:
http://php.net/manual/en/function.explode.php
http://php.net/manual/en/function.implode.php
(IMO, einfacher und erkennbarer als Pythons Split/Join)

Ich bin sehr für ridiculousfishs "keine Behandlung von Leerzeichen" in Variablen (sollte mit dags Vorschlag 1 kompatibel sein, wenn ich ihn richtig verstehe, da er davon spricht, Leerzeichen selbst zu zitieren). Variablen in Anführungszeichen setzen zu müssen, um sie zusammenzuhalten (sozusagen, damit sie nicht "explodieren"), ist das Wichtigste, was ich an Posix-Muscheln hasse. ** Explosion sollte explizit sein!

Mit anderen Worten, keine implizite Array-Interpretation, also

count (echo a\nb)

würde 1 ausgeben, wohingegen, wenn ich tatsächlich diese Art von String-Verarbeitung durchführen möchte,

count (explode \n (echo a\nb))     #Look ma, no IFS

würde 2 ausgeben.
Wie man erraten kann, gibt es einen umgekehrten Befehl, implodieren. Dies würde 1 ausgeben:

count (implode \n (explode \n (echo a\nb)))

Als Bonus bekommen wir dann einige String Processing Primitives!
Suchen & Ersetzen:

implode 'replace' (explode 'search' $mystring)

Basisname:

(explode / $mypath)[-1]

** Was Posix-Shells wirklich wirklich wollen, ist, Ihre Variablen zu explodieren, Ihre Daten als Befehle zu essen und Sie zu töten! Wie oft haben Sie beim Schreiben umfangreicher Shell-Skripte innegehalten, um darüber nachzudenken, wie viele Fehler Sie gerade erstellen, und über eine sicherere Programmiersprache wie C nachgedacht?

@xfix Danke für deinen Code, funktioniert wie ein Zauber!

Eine weitere großartige Syntax, die von $ wegläuft, ist das, was Swift für die String-Interpolation vorschlägt:

set CPUINFO "\(cat /proc/cpuinfo)"

Ich bin gegen ((...)) , #{...} , {...} , "\(...)" . Und "$(...)" ist das Besteda $ bereits ein spezielles Symbol für Variablen ist . Wenn wir auch die Syntax der Befehlsersetzung ohne Anführungszeichen in $(...) ändern, wäre das besser. Dann können wir () in test (...) -a (...) ohne Escape verwenden.

Vielleicht übersehe ich etwas, aber ist das nicht schon behoben?

Aus der Dokumentation :

Wenn die Ausgabe mehr als eine Zeile lang ist, wird jede Zeile um einen neuen Parameter erweitert. Wenn Sie IFS auf die leere Zeichenfolge setzen, wird die Zeilenaufteilung deaktiviert.

Die Lösung lautet also:

> count (echo one\ntwo\nthree)
3
> begin; set -l IFS; count (echo one\ntwo\nthree); end
1

@imgx64 : Das wurde hier schon einmal erwähnt.

Während das Mucking mit $IFS das Problem löst, ist es äußerst umständlich.

# echo 1 2 3 | read -z flatvar; count $flatvar
1

LGTM, dxlr8r!

Während read -z bis zu NUL liest, wäre natürlich eine Option, immer bis EOF zu lesen, eine etwas geeignetere Lösung für diesen Anwendungsfall.

Was verhindert, dass dieses Problem gelöst wird? Es scheint, als seien mehrere vernünftige Wege nach vorne vorgeschlagen worden. Können wir einen auswählen und damit weitermachen?

Ich werde dafür stimmen, () zugunsten von $() .

@dxlr8r danke für den Tipp zu read . Nützliche Problemumgehung.

Was verhindert, dass dieses Problem gelöst wird? Es scheint, als seien mehrere vernünftige Wege nach vorne vorgeschlagen worden. Können wir einen auswählen und damit weitermachen?

Nun, wir müssten tatsächlich einen auswählen und dann muss jemand den Code schreiben. Oder jemand schreibt Code, auf den wir uns einigen können.


Ich persönlich bin jetzt ziemlich fest davon überzeugt, dass der Wechsel zu "$()" die richtige Lösung ist.

  • Das Nichtaufteilen bei Zeilenumbrüchen wäre eine schrecklich bahnbrechende Änderung. Sehen Sie sich einfach unsere Skripte an. Viele unserer Befehlsersetzungen verwenden diese Funktion, und um herauszufinden, welche genau, müsste alles geprüft werden. Außerdem müssten die Benutzer all das auch tun, und es müsste ein Flag-Day-Übergang sein (dh Fish 3.0 wird veröffentlicht, jeder muss alles dafür neu schreiben, einige Leute sind immer noch auf 2.X). Sehen Sie, was mit Python passiert ist.
  • Das Aufteilen bei Zeilenumbrüchen ist ein guter Standard. Unix arbeitet normalerweise auf einer One-Thing-per-Line-Basis. Zum Beispiel gibt grep einen Treffer pro Zeile aus, git log hat einen "--oneline"-Schalter, um einen Commit pro Zeile auszugeben, was in Skripten sehr nützlich ist. Konfigurationsdateien sind normalerweise zeilenbasiert. Unser eigenes commandline druckt ein Token pro Zeile, wenn es nach Tokens aufgeteilt werden soll, ........ ("Alles Leerzeichen" ist nicht praktikabel, weil dies zu Unsinn wie Bashs Problemen mit Leerzeichen führt).
  • $() wäre innerhalb und außerhalb von doppelten Anführungszeichen verwendbar, ohne die Liste der Zeichen zu ändern, die eine Erweiterung beginnen (und daher maskiert werden müssen). Dies bedeutet auch, dass es die einzige Syntax sein könnte, um Befehlsersetzungen zu kennzeichnen.
  • $() wäre dieselbe Syntax wie POSIX und innerhalb von doppelten Anführungszeichen hätte dieselbe Semantik. Obwohl wir uns nicht an POSIX halten _müssen_, wenn die POSIX-Syntax funktioniert, warum etwas anderes erfinden? Wir verwenden nicht if $COMMAND; then $COMMAND; fi und for $COMMAND; do $COMMAND; done , weil es schöner ist, nur einen Blocktrenner zu haben (das ist das Paar begin und end ), aber wir tun es mach $COMMAND $argument1 $argument2 , weil das eine perfekt cromulente Art ist, es zu schreiben.

Ich glaube, all dies zusammen bedeutet, dass $() besser ist als alle anderen Vorschläge.


Also, was ist mein konkreter Vorschlag?

Führen Sie zuerst $() als alternative Befehlsersetzungssyntax ein. Ohne Anführungszeichen würde dies so funktionieren, wie es derzeit () tut - es würde die Ausgabe in Zeilenumbrüche aufteilen und jede Zeile als separates Argument übergeben. In doppelten Anführungszeichen würde die gesamte Ausgabe unverändert übergeben, einschließlich Zeilenumbrüche.

Dies würde bedeuten, dass Sie es als Drop-in-Ersatz für () verwenden könnten, und wenn Sie die mehrzeilige Ausgabe wörtlich speichern wollten, würden Sie einfach set var "$(COMMAND)" tun.

Fangen Sie auch an, () als veraltet zu markieren. Fügen Sie der Anzeige von Fisch XYZ einen Hinweis mit einer Frist bei. Beginnen Sie bei einer späteren Version mit einer Warnung, wenn () direkt in der Befehlszeile verwendet wird. Bei einer noch späteren Veröffentlichung immer Startwarnung. Entfernen Sie es dann, wenn die Frist abgelaufen ist. Wir wollen keine ```` (Backtick) Situation.


Nachtrag:

Ein verwandtes Ding trennt sich von etwas anderem. Wenn mein Vorschlag verwendet wird, könnte dies zumindest mit string split $CHAR "$(COMMAND)" geschehen. Außerdem wäre es möglich, string split in einer Befehlsersetzung zu etwas Besonderem zu machen, sodass $(COMMAND | string split $CHAR) nicht am Zeilenumbruch, sondern an $CHAR geteilt wird. Ich weiß nicht, wie das mit Anführungszeichen interagieren würde.

Meine 2 Cent. Ich mag den $()-Ansatz persönlich nicht, zu verwirrend für neue Leute, die von Bash usw. kommen und nicht benötigt werden, wie ich es sehe. Fügen Sie _set_ und _read_ ein Flag hinzu, das sollte ausreichend sein.

Ich persönlich mag den $()-Ansatz nicht, zu verwirrend für neue Leute, die von Bash kommen

Wieso den? Mit dem, was ich vorschlage, wäre der _einzige_ Unterschied zwischen $() von fish und bash, dass es, wenn es nicht in Anführungszeichen steht, nur bei Zeilenumbrüchen und nicht bei "irgendeinem" Leerzeichen geteilt wird (ich denke, es ist eigentlich $IFS oder Zeilenumbruch, Tabulator und Leerzeichen - nicht non -Leerzeichen und so). Das ist leicht erklärt (und AFAIK auch, was zsh standardmäßig tut).

Meine 2 Cent. Ich persönlich mag den $()-Ansatz nicht, zu verwirrend für neue Leute, die von Bash kommen

Hmm, ich habe die andere Schlussfolgerung, dass $() Leuten, die von Bash kommen, vertrauter wäre. Ich meine, es _ist_ die gleiche Syntax wie Bash.

Das ist das Problem, das dazu führen kann, dass Leute nur $() verwenden. Und um ehrlich zu sein, verstehe ich den Punkt nicht, () ist in Ordnung so wie es ist. Und hier geht es darum, Daten auf eine andere Weise zu speichern, nicht zu verwechseln mit Sub-Shelling usw. Ich denke, das gehört zu _set_ und _read_, aber ich würde wegen der Funktionalität auch $() verwenden, aber ich glaube nicht, dass das so ist so sauber.

Das ist das Problem, das dazu führen kann, dass Leute nur $() verwenden.

Und das ist die Idee. Dass die Leute auf die Verwendung $() und wir in Zukunft () können, sodass nur noch eine Syntax übrig bleibt, um dies zu tun.

Oder meinst du, Leute könnten von bash kommen, $() verwenden und erwarten, dass es sich im Raum aufteilt? Nun, meiner Erfahrung nach sind 90 % der Raumaufteilung tatsächlich ein Fehler. IIRC Einige Projekte verlangen, dass alle Variablen in einem Shellscript in Anführungszeichen gesetzt werden. Zsh verfügt über die Option SH_WORDSPLIT (die nur für die Parametererweiterung gilt - die Befehlsersetzung, die im Gegensatz zu dem, was ich zuvor erwähnt habe, auch auf Leerzeichen aufteilt), um die Aufteilung auf Leerzeichen wieder zu aktivieren, und sie ist standardmäßig deaktiviert (und es wird dringend davon abgeraten).

Oder meinen Sie, dass Leute $() verwenden, wenn sie "$()" meinen? Nun, sie müssen entscheiden, wie sie das Argument aufteilen wollen. Ich sehe keinen Weg daran vorbei. Wenn wir set eine Rohrleitung hinzufügen oder read eine Option hinzufügen (die tatsächlich die gesamte Eingabe als ein Element nimmt, nicht nur bis 0), dann müssten Sie sich entscheiden, ob Sie das möchten auf Zeilenumbrüche aufgeteilt werden oder nicht, dh ob Sie COMMAND | set var oder set var (COMMAND) wollen (oder die "--das-ganze-Ding-als-eins-bitte"-Option lesen).

Wirklich, Sie haben einen Befehl, der eine Art Ausgabe erzeugt. Wir können einen Standard für die Handhabung anbieten, aber wenn Sie es anders wollen, müssen Sie das ausdrücken.

Und dafür gibt es eine schöne Symmetrie zwischen Comsubs und Variablenerweiterung - wenn Sie es aufteilen möchten (auf Zeilenumbrüche/Elemente), verwenden Sie keine Anführungszeichen. Wenn Sie es als eins wollen, zitieren Sie.

Ich verstehe den Sinn einfach nicht, wie gesagt. Warum sollte $() für () übernehmen? () ist ein klarer Bruch mit der Bash-Syntax, was verwirrend ist. Fish "wenige" Möglichkeiten, Dinge zu tun, sind seine Stärke, es tut, was es gut tut, und Sie haben keine 5 Möglichkeiten, eine if-Anweisung zu schreiben ( test, [], (), (()) usw.).

Ich sehe nicht, wie/warum das Präfix $ vor () es besser machen würde, was fügt das $ der Syntax hinzu? () ist sauberer, kürzer und weniger wie bash. Wenn Leute viele Funktionen wollen, aber dennoch mit der Bash-Syntax kompatibel sein möchten, gibt es dafür zsh.

Ich sehe nicht, wie/warum ein vorangestelltes $ vor () es besser machen würde, was fügt das $ der Syntax hinzu? () ist sauberer, kürzer und weniger wie bash

Das "$" ermöglicht die Lösung dieses Problems. "$"s sind innerhalb doppelter Anführungszeichen bereits etwas Besonderes, also können wir diese Tatsache wiederverwenden, um eine Syntax zum Befehlen von Ersetzungen zu ermöglichen, die auch in doppelten Anführungszeichen funktioniert, _ohne die Anzahl der Sonderzeichen zu erhöhen_. Dies ist dann eine natürliche Notation für Befehlsersetzungen, die nicht geteilt werden.

Es würde auch das zukünftige Entfernen von () ermöglichen und auch die Menge an Sonderzeichen im Allgemeinen reduzieren. Das wäre schön, weil jedes Sonderzeichen speziell behandelt werden muss - insbesondere müssen sie über (einfache) Anführungszeichen oder \ werden.

Es würde auch keine Ausdrücke hinzufügen, die _anders_ funktionieren, da $() derzeit sowohl ohne Anführungszeichen als auch in doppelten Anführungszeichen ein Fehler ist.

Fish "wenige" Möglichkeiten, Dinge zu tun, sind seine Stärke, es tut, was es gut tut, und Sie haben keine 5 Möglichkeiten

Die Idee ist, dass die Bereitstellung von zwei Optionen eher eine vorübergehende Sache ist, damit Skripte nicht sofort neu geschrieben werden müssen (und mit zwei Versionen, eine für Fische vor und eine für Fische nach dieser Änderung). Das müssten wir richtig kommunizieren.

() ist ein klarer Bruch mit der Bash-Syntax, was verwirrend ist

$() ist nicht von Natur aus verwirrend. IMO ist es einer der saubereren Teile der Bash-Syntax.

Was fügt das $ der Syntax hinzu?

Es ermöglicht Ihnen, einen Unterbefehl innerhalb einer Zeichenfolge in Anführungszeichen auszuführen, ähnlich wie Sie $FOO eine Variable innerhalb einer Zeichenfolge in Anführungszeichen erweitern können.

Ich denke, das Zitieren sollte nur für Zeichenfolgen gelten. Und ich zitiere die Designdokumente:

Die vielen Posix-Zitatstile sind albern, besonders $''.

Ich stimme @Soares zu, dass wir neue Befehle/Argumente hinzufügen sollten, anstatt sie neu zu gestalten. Dieses Thema kann leicht gelöst werden, ohne den Kern des Fisches zu verändern.

Ich stimme @Soares zu, dass wir neue Befehle/Argumente hinzufügen sollten, anstatt sie neu zu gestalten. Dieses Thema kann leicht gelöst werden, ohne den Kern des Fisches zu verändern.

Ich bin auch mit dieser Lösung zufrieden. Aber nur um den Anwalt des Teufels für die andere Lösung zu spielen, sage ich, dass ich #3329 eingereicht habe, weil das Verhalten des Fisches überraschend war. Ich habe erwartet, dass set -x foo (cat foo.txt) den Inhalt von foo.txt nicht standardmäßig verstümmelt. Ich habe versucht, den Befehl in Anführungszeichen zu setzen, aber festgestellt, dass es ohne $ oder ein anderes Escape-Zeichen nicht möglich wäre. Dann versuchte ich bash, erhielt das erwartete Verhalten und kam zu dem Schluss, dass Fisch einen Fehler hatte. Dies wäre also ein Argument der Intuitivität, wie Benutzer erwarten würden, dass die Dinge funktionieren.

Ich mag die Idee, Flags für set und read zu haben, um das Aufteilungsverhalten zwischen Aufteilung bei Zeilenumbrüchen oder nicht zu ändern, aber das scheint nur die halbe Lösung zu sein, da die Befehlsersetzung nicht betroffen wäre (und wir wollen die Leute nicht zwingen, die Ausgabe in einer Variablen zu erfassen, bevor sie sie als Argumente an einen Befehl weitergibt).

Ich denke, die Verwendung der $() -Syntax ist sinnvoll. Es würde nicht nur ermöglichen, dieses Problem zu beheben, indem die () -Syntax verworfen oder parallel beibehalten wird, sondern ich denke, es spricht auch etwas für die Verwendung der $() -Syntax im Allgemeinen: Es bedeutet, dass Substitutionen Beginnen Sie immer mit einem $ , was der Übersichtlichkeit dient. Die () -Syntax ist zwar sauberer (und sogar schwammig, wenn Sie das mögen), sie unterscheidet sich jedoch stark von anderen Shells und ist nicht mit der Variablenersetzung vereinbar. Ich denke, das Einsparen eines Zeichens beim Tippen und optische Sauberkeit sind nicht so wertvoll wie Klarheit und Konsistenz.

Ich denke, dieses Thema ist wichtig. Sehen wir voraus, dass die "$()" -Syntax bald verfügbar sein wird?

Das ist auch sehr frustrierend für mich, um mit Meteor zu arbeiten, muss ich manchmal ein ziemlich langes settings.json in eine Umgebungsvariable wie folgt einlesen:

env METEOR_SETTINGS=(cat settings.json) meteor run ....

derzeit kann ich das nicht wirklich und muss mich ein bisschen in bash fallen lassen.

Mit dem, was ich vorschlage, wäre der einzige Unterschied zwischen dem $() von fish und dem von bash, dass es, wenn es nicht in Anführungszeichen steht, beim Zeilenumbruch geteilt wird

Seien wir dankbar, dass das Zitieren von Variablen in fish nicht so notwendig ist wie in bash. Wie Wikipedia es zusammenfasst :

Quoting not necessary to suppress word splitting and glob interpretation.
Instead, quoting signifies serialization.

Der einzige Grund, warum wir Bash-Programmierern getrost sagen können: „ Komm zum Fischen, hier kannst du alles vergessen, was du über Zitieren weißt “, ist, dass Zitieren in Fischen etwas anderes bedeutet. Aber wenn wir hinzufügen müssen „ Oh, außer Befehlssubstitutionen “, dann ist es weniger überzeugend.

Das ist ein Grund für einen sauberen Bruch und dafür, dass die Dinge Bash-Programmierern nicht zu vertraut erscheinen.

Das ist ein Grund für einen sauberen Bruch und dafür, dass die Dinge Bash-Programmierern nicht zu vertraut erscheinen.

Ich würde lieber die Einstellung haben, lasst uns Bash vergessen und eine gute Syntax entwerfen, anstatt zu versuchen, uns so von Bash zu unterscheiden und unsere Möglichkeiten einzuschränken.

… aber ist eine gute Syntax nicht eine, die sich ausdrücklich mit der Aufteilung befasst?

… aber ist eine gute Syntax nicht eine, die sich ausdrücklich mit der Aufteilung befasst?

Eine gute Syntax ist eine, bei der Sie wissen, was Sie schreiben müssen, wenn Sie sie schreiben müssen, und wenn Sie sie lesen müssen, wissen Sie, was sie tut.

Explizit zu sein funktioniert dafür, ist aber nicht notwendig und kann auch _zu_ explizit sein.

Warum wollen wir also nicht die erforderliche explizite Aufteilungssyntax? Nun, wir können die Standardeinstellung _nicht_ ändern (wenn keine Teilung angegeben ist), weil das die Welt (zumindest der Fische) zerstören würde. Wir würden es auch nicht wollen, weil die Standardeinstellung (Aufteilen bei Zeilenumbruch) in 90 % der Fälle richtig ist - Unix ist traditionell zeilenorientiert, daher ist es schön, Zeilen standardmäßig separat zu handhaben. Das Erfordernis eines expliziten "split this on newlines please" ist eine lästige explizite Syntax.

Was ist also "gute Syntax" an $() ? Es stimmt mit $var überein. echo $var teilt sich auf, während echo "$var" dies nicht tut. Das heißt, wer letzteres kennt, kann ersteres erraten. Wenn Sie es schreiben müssen, stehen die Chancen gut, dass Sie darauf kommen, und wenn Sie es lesen, stehen die Chancen gut, dass Sie es verstehen.

Der einzige Grund, warum wir Bash-Programmierern getrost sagen können: «Kommen Sie zum Fischen, hier können Sie alles vergessen, was Sie über das Zitieren wissen», ist, dass Zitieren in Fisch etwas anderes bedeutet. Aber wenn wir hinzufügen müssen «Oh, außer Befehlssubstitutionen», dann ist es weniger überzeugend.

Nein, das würde noch gehen. Ohne Anführungszeichen wird das Zeug auf einem guten Standard aufgeteilt. Wenn Sie etwas anderes brauchen, fügen Sie Anführungszeichen hinzu und tun Sie, was Sie wollen.

Das Problem mit dem Zitieren in Bash ist nicht, dass es existiert, sondern dass es eine „Bitte das Richtige tun“-Syntax ist. Für Variablen benötigen Sie fast immer Anführungszeichen. Der Standard ist das, was Sie nur in besonderen Fällen wollen, und die spezielle Syntax ist das, was Sie normalerweise wollen. Wenn eine normale Konversation so wäre, müssten Sie nach jeder Frage "und schreien Sie die Antwort nicht an" hinzufügen.

ähnlich wie pipeset von @xfix oben, hier ist, was ich mir ausgedacht habe, um mein Problem zu lösen:

function withsub
  if [ (count $argv) -lt 2 ]
    echo need at least two arguments!
    return 1
  end
  set -l cmd
  set -l val (eval $argv[1])
  for arg in $argv[2..-1]
    if [ "$arg" = "[]" ]
      set cmd $cmd "$val"
      else
        set cmd $cmd "$arg"
      end
    end
  eval (string escape -- $cmd)
end

Das OP-Problem betrifft mich nicht wirklich, es in eine Variable zu setzen und die Variable wie "$var" zu verwenden, löst dieses Problem, aber die Befehlsersetzung kann nicht zitiert werden.

$ withsub "seperated a b c" seperated 1 [] 2
1
--
a -- b -- c
--
2

wenn du sowas hast

function seperated
  echo $argv[1]
  for arg in $argv[2..-1]
    echo --    
    echo $arg
  end
end

In der Zwischenzeit wollte ich, dass ein Zeilenumbruch in einer Variablen gespeichert wird, damit ich ihn sauberer in einen screen -X "...$newLine" -Befehlsaufruf werfen kann.

Da () nicht kooperierte, habe ich dies verwendet, falls jemand anderes es auch braucht: printf "\n" | read -z newLine;

Danke @dxlr8r!

@ Pysis868 : In diesem Fall würde set newLine \n einfach funktionieren - oder später screen -X "..."\n .

Ich bin gerade wieder darauf gestoßen und obwohl ich vor 4 Monaten an dieser Ausgabe teilgenommen habe, war ich völlig überrascht und es dauerte eine Stunde, um herauszufinden, was los war. Und sehen Sie sich oberhalb dieses Kommentars diese anderen Commits an, die sich auf dieses Problem beziehen, da fish so wie es aussieht nicht intuitiv ist .

Lassen Sie uns den Designfehler anerkennen, beheben und weitermachen. Milestone fish-future erkennt die Schwere dieses Designfehlers nicht an.

Lassen Sie uns den Designfehler anerkennen, beheben und weitermachen

Wir würden gerne eine Pull-Anfrage von Ihnen, @andrewrk , oder jemand anderem, der diese Funktion implementiert, überprüfen. Weitere Mitwirkende sind immer willkommen , da zu wenige regelmäßig Beiträge leisten .

@krader1961 Ich würde gerne einen Beitrag leisten, aber ich weiß nicht, wo ich anfangen soll. Gibt es etwas darüber, wie der Code organisiert ist, auf das ich mich beziehen kann?

@spinningarrow : Nicht wirklich. Wir haben ein Dokument, wie man beitragen kann. Aber das meiste davon ist für Leute, die beabsichtigen, kontinuierlich Veränderungen beizutragen. Wenn Sie daran interessiert sind, selten Beiträge zu leisten, brauchen Sie sich nicht wirklich um diese Richtlinien zu kümmern, da sich die Kernentwickler um Stil, Lint und ähnliche Probleme kümmern. Der einzige Weg, um zu erfahren, wie der Code organisiert ist, besteht darin, das Repo git clone zu durchsuchen und es zu erkunden.

Warum sollten wir den Fisch ändern, wenn er, wie @imgx64 betont hat, bereits gelöst und dokumentiert ist als "Wenn Sie IFS auf die leere Zeichenfolge setzen, wird die Zeilenaufteilung deaktiviert."

Ein ähnliches Ergebnis kann auch mit read: ls | read -z test erzielt werden.

Da stimme ich @faho nicht zu

Während das Mucking mit $IFS das Problem löst, ist es äußerst umständlich

Ich finde es nur etwas umständlich. Aber sicherlich kann das Ändern von IFS oder die Verwendung von read -z nicht so umständlich sein wie das Ändern der Syntax und Semantik von fish .

Danke für das Feedback, @GReagle , aber du bist definitiv in der Minderheit. Der einzige Grund, warum dies noch nicht geschehen ist, ist, dass es mehr Arbeit erfordert als die meisten Änderungen und technisch nicht abwärtskompatibel ist, selbst wenn wir weiterhin die aktuelle reine (cmd) -Syntax unterstützen. Wenn wir eine Veröffentlichung von Fish 3.0 (dh Major) herausbringen, wird diese definitiv enthalten sein. Auch wenn es bedeutet, einige andere Dinge, die wir gerne in einer Hauptversion sehen würden, nicht zu tun.

Außerdem sollte es niemals notwendig sein, IFS im Fischskript zu manipulieren. Diese Var sollte sterben und ihre Entfernung ist etwas anderes, was wir für eine Hauptversion in Betracht ziehen sollten. Wann immer ich musste oder es früher gesehen habe, zu ändern, wie Strings in anderen Shells (ksh, bash, zsh) in Wörter aufgeteilt werden, wollte ich mich übergeben. Es ist das Aushängeschild für das, was mit POSIX 1003-kompatiblen Shells nicht stimmt.

Ich weiß nicht viel über IFS und seine Manipulation. Warum ist es schlecht?

Ich weiß nicht viel über IFS und seine Manipulation. Warum ist es schlecht?

Ein paar Dinge:

Es kann nur für einen ganzen Befehl gesetzt werden, also zB

set -l var (cat file1) (cat file2)

bedeutet, dass Sie nur beide Dateien teilen können oder nicht (oder Befehlsersetzungen im Allgemeinen) - es gibt keine andere Möglichkeit, nur Datei1 zu teilen, als dies über zwei Befehle zu tun.

Um es zu verwenden, müssen Sie es entweder zurücksetzen ( set -l oldifs $IFS; set -l IFS; command; set IFS $oldifs ) oder mit dem Scoping herumspielen (z. B. einen begin; end Block nur dafür einführen).

Es zieht auch Double-Duty - wenn IFS nicht gesetzt oder auf leer gesetzt ist (und nur das, der tatsächliche Wert spielt keine Rolle), werden Befehlsersetzungen nicht geteilt, und es wird verwendet, um für read zu teilen - zB set -l IFS =; read -l key value < file.ini um eine Ini-Datei zu lesen).

Es ist auch irgendwie unvereinbar damit, wie Fische Dinge tun - wir verwenden Variablen nicht allzu sehr, um das Verhalten zu ändern.

All das bedeutet, dass es leicht zu vermasseln ist und mehr Pflege als gewöhnlich erfordert.

Es ist nicht so, dass es für den Zweck ungeeignet wäre – ja, es kann verwendet werden, um Befehlsersetzungen nicht aufzuteilen – es ist, dass es eine umständliche Schnittstelle ist.

Oder fragen Sie sich, was Ihnen mehr gefällt:

set -l oldifs $IFS
set -l IFS =
somecommand | read -l key value
set IFS oldifs

# or

somecommand | read -l -f = key value # the "-f" option is hypothetical at this point

und

set -l oldifs $IFS
set -l IFS
set -l var (somecommand)
set IFS $oldifs
set var $var (somecommand2)

# or
set -l var (somecommand2)
begin
    set -l IFS
    set var (somecommand1) $var
end

# or

set -l var "$(somecommand)" $(somecommand)

Hinweis: Es gibt natürlich andere Möglichkeiten, um anzuzeigen, dass die Ausgabe eines Befehls nicht geteilt werden soll - einige Dinge wurden in diesem Thread erwähnt. Aber viele von ihnen wären besser als umständliche Wenns-Tricks. Auch das Zurücksetzen von IFS kann in vielen Fällen übersprungen werden - zB weil die Funktion sowieso beendet ist.

@faho danke für diese lehrreiche Erklärung.

@faho Das war eine tolle Erklärung - danke!

Irgendein Update dazu? Das nicht zu haben ist im Moment extrem ärgerlich.

Ich möchte, dass IFS entfernt wird und standardmäßig eine leere Zeichenfolge ist. Wenn jemand die Befehlsausgabe als separate Parameter aufteilen möchte, sollte er ohne Ausnahmen string split <pattern> verwenden.

string split funktioniert nur wegen IFS .

> begin
    set -l IFS
    set -l wat (string split x fooxbarxbaz)
    set -S wat
end
$wat: set in local scope, unexported, with 1 elements
$wat[1]: length=11 value=|foo\nbar\nbaz|
$wat: not set in global scope
$wat: not set in universal scope

Oder anders ausgedrückt, damit dies funktioniert, müssten wir eine spezielle Klasse von Builtins einführen, die unabhängig von IFS mehrere Argumente erzeugen können (und dann einen Weg finden, diese Funktion auch für fishscript verfügbar zu machen ). Und das Entfernen von IFS würde am Ende dazu führen, dass eine wirklich riesige Menge an Fishscript beschädigt wird.

Vielleicht gibt es für meinen Anwendungsfall einen anderen Weg, dies zu umgehen; Ich sehe keinen ohne mehrzeilige Ausgabeunterstützung für eine Variable.

Ich verwende screenfetch für eine (ziemlich) schnelle Anzeige des Systemstatus beim Öffnen eines Terminals. Ich habe es ursprünglich faul am Ende von config.fish eingerichtet:
set fish_greeting ""
Echo
Bildschirmabruf

Wenn Sie es auf diese Weise tun, hat dies jedoch negative Auswirkungen, wenn Sie Dateien über scp auf dieses Konto verschieben, wenn es aktiv ist (die ersten paar Zeilen des Bildschirms werden anstelle der Fortschrittsbenachrichtigung angezeigt; die Datei wird nicht kopiert). Also habe ich versucht, screenfetch als fish_greeting zu setzen - scp war als Ergebnis glücklich, aber aufgrund fehlender Zeilenumbrüche sind meine Augen nicht.
set fish_greeting (Bildschirmabruf)

Ich habe versucht, string split '\n' zu leiten, wie in Ausgabe 2490 erwähnt, ohne Erfolg. Um sicherzustellen, dass dies kein Problem ist, das nur für die Ausgabe von screenfetch gilt, habe ich dieselben Methoden mit cat /proc/cpuinfo anstelle von screenfetch ausprobiert. Die Zeilenumbrüche werden jedes Mal entfernt.

@joelmaxuel Sie sollten immer alles prädizieren, was nur für interaktive Shells auf if status is-interactive getan werden sollte. Sie können auch mehrzeilige Ausgaben erfassen, ohne sie ganz einfach an Zeilenumbrüchen aufzuteilen: screenfetch | read -z fish_greeting .

@faho @zanchey Ich denke, dies kann mit der Ablehnung von IFS, der Entscheidung, die $(...) -Syntax nicht zu unterstützen, den Verbesserungen an read -z und anderen geschlossen werden?

Viel zu lesen ... was mir fehlt, ist eine Möglichkeit, dies zu tun:

export GOOGLE_CLOUD_SQL_CREDENTIALS=(cat google-cloud-sql-credentials.json)

und dies zu tun, würde die Verwendung von gegoogelten Beispielen erleichtern:

export GOOGLE_CLOUD_SQL_CREDENTIALS=$(cat google-cloud-sql-credentials.json)

@mhubig einfachster Weg ab Fish 3.0:

export GOOGLE_CLOUD_SQL_CREDENTIALS=(cat google-cloud-sql-credentials.json | string split0)

split0 bedeutet, auf null Bytes anstelle von Zeilenumbrüchen aufzuteilen. Sie können einen kleinen, nicht spaltenden Helfer machen:

function cat1 ; cat $argv | string split0 ; end
export GOOGLE_CLOUD_SQL_CREDENTIALS=(cat1 google-cloud-sql-credentials.json)

obwohl ich glaube, dass wir das noch nicht wirklich abschließen können.

Vielleicht sollten wir dafür einen string nosplit Befehl hinzufügen? foo | string split0 ist ein netter Hack, aber er wird nicht ganz das tun, was wir wollen, wenn der String tatsächlich NULs enthält. Mir ist klar, dass wir NULs nicht in einer Variablen speichern (oder sie anscheinend als Argument an einen anderen Befehl übergeben können), aber es könnte nett sein, das eines Tages zu beheben.

Natürlich ist es immer noch irgendwie kludge zu sagen, dass die offizielle Methode zum Deaktivieren des Aufteilens von Zeilenumbrüchen darin besteht, zu einem magischen Befehl zu leiten. Ich würde dafür eine Art dedizierte Syntax bevorzugen.

Mir ist klar, dass wir NULs nicht in einer Variablen speichern (oder sie anscheinend als Argument an einen anderen Befehl übergeben können), aber es könnte nett sein, das eines Tages zu beheben.

Sooo .... wir können sie nicht als Argument übergeben, weil in Unix Argumente als ... NUL-terminierte Zeichenfolgen übergeben werden. Und wir können sie auch nicht als exportierte Variablen übergeben, weil das Export-Array in Unix ... NUL-terminiert ist.

Was die Verwendung von NULs in einer Shell sofort erheblich reduziert. Die einzige Möglichkeit wäre, sie intern zu speichern, sie an Builtins weiterzugeben (obwohl das allein ein bisschen eine Neuarchitektur ist) und dann zu erlauben, echo $var | thing zu tun. Das ist nicht sehr nützlich, weil Sie nicht oft mit "binären" Daten in einer Shell umgehen, aber es gibt einige Vorteile. IIRC, das macht zsh.

Aber... was würde nosplit jetzt tun? Es müsste entweder dasselbe tun wie string split0 (was Lügen bedeutet) oder die NULs beibehalten (was bedeutet, dass sie "automatisch" kürzen würden) oder manuell kürzen.

Ich würde wahrscheinlich die NULs behalten und Unix einfach seinen Lauf lassen.

Guter Punkt, dass Unix uns sie nicht als Argumente übergeben lässt, aber in der Vergangenheit wollte ich eine Zeichenfolge mit NULs in eine Variable stecken, um sie später zu einem Befehl zu leiten, wie z

set -l foo
if condition
    set foo (cmd-emitting-NULs)
else
    set foo (other-cmd-emitting-NULs)
end
echo "$foo" | xargs -0 …

Selbst wenn wir dieses Szenario nie unterstützen, ist es auf jeden Fall immer noch nicht offensichtlich, dass string split0 das Richtige ist, zumal dies nicht die Garantie bietet, dass die resultierende Variable nur einen Eintrag hat. Ich würde also erwarten, dass string nosplit buchstäblich keine Teilung durchführt, was dann ein automatisches NUL-Trunkierungsverhalten hervorrufen würde (und daher in Zukunft, wenn wir jemals anfangen, NULs beizubehalten, dann würde dies sie auch automatisch beibehalten).

Übrigens, nachdem ich darüber nachgedacht habe, bin ich geneigt, das jetzt string collect zu nennen, weil es alle Zeilen in einer einzigen Variablen "sammelt". Dies ist konzeptionell etwas sinnvoller, wenn man etwas wie foo=(echo $arrayVar | string collect) betrachtet.

Ich wollte nur einwerfen, dass dies Splunk betrifft:

(clear; '/Applications/Splunk/bin/splunk' start || touch "/tmp/splunk_start_failed_14856"); rm "/tmp/splunk_start_running_32525" 
fish: Command substitutions not allowed

@paragbaxi Ihr aufgelisteter Befehl hat nichts mit diesem Problem zu tun und ist einfach ein Fall, in dem Sie versuchen, eine Befehlssubstitution als gesamten Befehl statt als Argument zu verwenden, und Fish erlaubt dies nur in Argumentposition. Es ist nicht klar, warum Sie überhaupt eine Befehlsersetzung verwenden, Sie könnten einfach die Klammern in dem von Ihnen aufgelisteten Befehl löschen, aber wenn Sie die Befehle besonders gruppieren müssen, dann ist begin; commands_go_here; end dafür da .

Ich habe einen PR für string collect als https://github.com/fish-shell/fish-shell/pull/5943 eingereicht. Wie ich in der PR feststelle, denke ich nicht, dass dieser neue Befehl ausreichen sollte, um dieses Problem zu schließen, da es sinnvoll ist, eine dedizierte erkennbare Syntax dafür zu haben (ein string -Befehl ist nicht dort, wo ich Benutzer erwarten würde zuerst nachschauen, wenn Sie versuchen, herauszufinden, wie man mehrzeilige Ausgaben erfasst).

Siehe auch #5942, in dem ich argumentiere, dass string join die implizite Aufteilung seiner Ausgabe deaktivieren sollte, da es etwas wie set contents (cat filename | string join \n) ist, was Benutzer vermuten könnten, und doch string join \n am Ende tut gar nichts.

Mit dem neuen Befehl string collect sollten wir vielleicht einfach den Parser optimieren, um die versuchte Verwendung von $() in doppelten Anführungszeichen zu erkennen und stattdessen | string collect vorschlagen?

Ich habe neulich Amazons ECR verwendet, und um mich anzumelden, sagen sie mir, ich solle es tun

bash/zsch

$(aws ecr get-login --no-include-email --region us-east-1)

Ich habe es mit Fisch probiert:

(aws ecr get-login --no-include-email --region us-east-1)
fish: Command substitutions not allowed

Aber es heißt, dass Befehlsersetzungen nicht erlaubt sind. Kann ich etwas tun, damit das funktioniert?

@ngortheone was ist die Ausgabe dieser Funktion. Können Sie es in einer Datei speichern und uns zeigen?

Aber es heißt, dass Befehlsersetzungen nicht erlaubt sind. Kann ich etwas tun, damit das funktioniert?

@ngortheone : Dies ist das völlig falsche Thema dafür, aber Sie tun Folgendes:

Sie versuchen, die Ausgabe einer Befehlsersetzung als Befehl auszuführen. Fish erlaubt das nicht, aber es erlaubt das Ausführen von Variableninhalten.

So:

set -l var (aws ecr get-login --no-include-email --region us-east-1)
$var

oder nur

env (aws ecr get-login --no-include-email --region us-east-1)

@faho
Entschuldigung, das Problem schien relevant zu sein. Ist ein relevantes Thema offen?

Ich habe das oben vorgeschlagene versucht und es hat nicht funktioniert.

der Befehl selbst gibt einen anderen Befehl aus. Beispielausgabe (geschwärzt, da Anmeldeinformationen vorhanden sind)

~ aws ecr get-login --no-include-email --region us-east-1
docker login -u AWS -p VERYVERYLONGBASE64 https://1234567890123.dkr.ecr.us-east-1.amazonaws.com

Die Fehlermeldungen sind

 env (aws ecr get-login --no-include-email --region us-east-1)
env: output_of_command_here: File name too long

Oder manchmal enden base64-Anmeldeinformationen mit '==', dann bekomme ich

fish: Unsupported use of '='. In fish, please use ...

Entschuldigung, das Problem schien relevant zu sein.

Es ist nicht. Hier geht es darum, etwas Ähnliches wie something "$(somethingelse)" einzuführen.

Ist ein relevantes Thema offen?

Dies ist eines der Dinge, die auf Gitter besser funktionieren würden.

Aber wie auch immer:

Im Gegensatz zu bash et al teilt fish keine Befehlsersetzungen auf Leerzeichen auf, sondern nur Zeilenumbrüche. Sie werden sehen, dass es so etwas wie 'docker login -u AWS -p...' als ein Argument übergibt, weshalb es sich darüber beschwert, dass der Dateiname zu lang ist (wenn dies nicht der Fall wäre, würde es sich höchstwahrscheinlich beschweren, dass er nicht existiert).

Was Sie also wollen, ist, sie auf Raum aufzuteilen, also müssen Sie string split verwenden

 env (aws ecr get-login --no-include-email --region us-east-1 | string split " ")

@faho Es hat funktioniert, danke!

Der letzte diesbezügliche Kommentar ist über ein Jahr her. Dies ist immer noch ein großes fehlendes Feature, für das es keine gute/standardmäßige Methode gibt. Sind wir mit "$(echo $var)" nicht zufrieden? Warten wir auf etwas, dem geholfen werden kann?

Wir haben jetzt string collect , aber wie ich bereits sagte, denke ich, dass wir einen Erkennungsmechanismus für Benutzer hinzufügen müssen, bevor wir dieses Problem schließen können.

Ich stimme zu, dies offen zu halten. Am Ende werden wir wahrscheinlich die "$(cmd)" -Syntax unterstützen, aber es ist eine Menge Arbeit.

Der erste Schritt ist, denke ich, es zu entwerfen. In bash erstellt "$(cmd)" einen neuen Zitierkontext:

echo "$(echo "nested quotes!")"

wollen wir das in beliebiger Tiefe zulassen? Oder können wir mit etwas Nützlichem beginnen, das jedoch eingeschränkter ist und einfacher zu implementieren wäre?

Ich denke, wir können den Parser hacken, um doppelte Anführungszeichen auf $( zu schließen und sie nach dem passenden ) wieder zu öffnen.
echo "b$(...)c" wird also virtuell als echo "b"$(...)"c" tokenisiert.
Auf diese Weise erhalten wir kostenlos Verschachtelung, Syntaxhervorhebung und Vervollständigung - siehe meine Implementierung .

Der Preis dafür ist die komplexere Quoting-Syntax - eine einzelne Regex reicht nicht mehr aus, um solche Zeichenfolgen korrekt hervorzuheben.
Wahrscheinlich gibt es fertige Lösungen, um diese Art von Syntax in den meisten Textmarkern von Drittanbietern zu unterstützen.

Wenn wir keine Anführungszeichen innerhalb "$()" unterstützen, bleibt die Hervorhebungssyntax unverändert.
Die Verschachtelung ist weniger wichtig als in bash, aber es fühlt sich natürlich an, sie irgendwann einzubeziehen.

Ich bin mir nicht sicher, was ich mit $() ohne Anführungszeichen tun soll. Ich denke, es sollte dasselbe bedeuten wie "$()" , aber frühere Vorschläge verwenden die Bedeutung () . Wir können es auch vorerst ohne Unterstützung belassen.


Alternativ, wenn wir vermeiden wollen, die Anführungszeichen-Syntax komplexer zu machen, könnten wir nur $() ohne Anführungszeichen unterstützen:

echo "no interpolation "$(echo "(but nested quotes)")" for you"

Das mag weniger elegant sein, erfordert aber kein Verständnis des Konzepts der Befehlssubstitutionen in Anführungszeichen.
In Situationen, in denen keine Anführungszeichen für den umgebenden Text erforderlich sind (wie das OP), ist es auch prägnanter.

Wir könnten versuchen, die Eingabe so bequem wie die Bash-Version zu machen:
Wenn der Benutzer "foo$( eingibt, könnte Fish stattdessen einfach "foo"$( einfügen ...

Als jemand, der mit Shell-Scripting eher unerfahren ist, denke ich, dass es nicht intuitiv ist zu sehen, warum eine Variable nicht "einfach die Ausgabe eines Befehls abfangen" kann.

Es scheint, als würde string collect immer noch die Eingabe manipulieren, indem es die abschließende neue Zeile entfernt.

# 1
> printf "A\nB\nC\n" | od -a
0000000   A  nl   B  nl   C  nl
0000006

> set var (printf "A\nB\nC\n")
# 2 Expected (1)
> printf "$var" | od -a
0000000   A  sp   B  sp   C
0000005

> set var (printf "A\nB\nC\n" | string collect)
# 3 Expected (1)
> printf "$var" | od -a
0000000   A  nl   B  nl   C
0000005

Es scheint also, dass string collect $() Probleme repliziert. Das wird wirklich chaotisch, wenn Sie versuchen, etwas mit CRLF , wie z. B. einen HTTP-Header von curl. Ich mag Fisch wirklich sehr, aber dieses Problem macht es sehr unpraktisch für die Skripterstellung.

Es scheint, als würde string collect immer noch die Eingabe manipulieren, indem die abschließende neue Zeile entfernt wird.

@jos128 : string collect -N , wenn Sie wirklich den abschließenden Zeilenumbruch haben müssen.

In vielen, vielen, vielen Fällen tun Sie das nicht.

In vielen, vielen, vielen Fällen tun Sie das nicht.

@faho : Das mag stimmen, aber es ist nicht das, was naiv erwartet wird . Persönlich denke ich, dass das Entfernen des letzten Zeilenumbruchs die Option sein sollte. Wie gesagt, es wird wirklich unintuitiv mit CRLF , da das selbst gut druckt, aber sehr verschleiert ist, wenn es in einem Terminal verarbeitet wird. Z.B

> set var (curl "https://example.com" -D - -s -o /dev/null)
> printf "$var"
> printf "$var" | grep server
 content-length: 1256g)

Natürlich reicht in diesem Fall string collect aus, aber ich hoffe, Sie sehen, dass dies nicht erwartet wird . Zumal es in Bash gut funktioniert. Bei HTTP-Headern kann das Entfernen des letzten Zeilenumbruchs natürlich noch bedeutendere, aber versteckte Probleme im Downstream verursachen.

Entschuldigung, weil ich OT bin. Ich dachte, die Perspektive von jemandem, der Interna nicht für selbstverständlich hält, könnte für Designentscheidungen von Wert sein. Mir erscheint dieses Verhalten von Fischen obskur, daher scheint es für einen Außenstehenden nicht mit dem Designmuster von Fischen übereinzustimmen.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen