xargs und vi - "Die Eingabe erfolgt nicht über ein Terminal"

14

Ich habe ungefähr 10 php.iniDateien auf meinem System, die sich überall befinden, und ich wollte sie schnell durchsuchen. Ich habe diesen Befehl ausprobiert:

locate php.ini | xargs vi

Aber viwarnt mich Input is not from a terminalund dann wird die Konsole wirklich komisch - danach muss ich drücken, um :q!zu beenden viund dann die Verbindung zur SSH-Sitzung zu trennen und die Verbindung wiederherzustellen, damit sich die Konsole wieder normal verhält.

Ich denke, ich verstehe irgendwie, was hier passiert - im Grunde ist der Befehl beim viStarten noch nicht beendet, so dass der Befehl möglicherweise noch nicht beendet ist und vinicht glaubt, dass sich das Terminal im normalen Modus befindet.

Ich habe keine Ahnung, wie ich das beheben soll. Ich habe Google und auch unix.stackexchange.com mit Pech durchsucht.

cwd
quelle
Als Randnotiz können Sie ausführen reset, um Ihr Terminal zurückzusetzen, wenn es vermasselt wird (Sie müssen die Verbindung nicht von der SSH-Sitzung trennen).
wisbucky

Antworten:

12
vi $(locate php.ini)

Hinweis: Dies tritt auf, wenn Ihre Dateipfade Leerzeichen enthalten, entspricht jedoch funktional Ihrem Befehl.
Diese nächste Version behandelt Leerzeichen ordnungsgemäß, ist jedoch etwas komplizierter (Zeilenumbrüche in Dateinamen werden sie jedoch weiterhin beschädigen).

(IFS=$'\n'; vi $(locate php.ini))


Erläuterung:

Was passiert ist, dass Programme ihre Dateideskriptoren von dem Prozess erben, der sie erzeugt hat. xargshat seinen STDIN mit dem STDOUT von verbunden locate, vihat also keine Ahnung, in was der ursprüngliche STDIN wirklich steckt.

Patrick
quelle
2
xargs ist wunderbar, eines meiner Lieblingswerkzeuge - es ist einfach nicht für die Verwendung mit Programmen geeignet, die stdin für etwas anderes als einen Datenfeed verwenden. Ich mag deine Antwort und deine Erklärung anders, also trotzdem +1 :)
Cas
@CraigSanders Ich mag es nicht, weil es zu leicht ist, es zu missbrauchen (unsachgemäß zu verwenden) und am Ende zu brechen. Ich bin noch nie auf etwas gestoßen, das ich unbedingt benutzen musste xargs, was nicht direkt mit der Shell (oder find) gemacht werden konnte. Ich kann mir jedoch Fälle vorstellen, in denen dies die beste Lösung wäre. Also, solange Sie verstehen, was xargstut, wie es die Argumente aufteilt, wie es das Programm ausführt usw. und es richtig verwendet, würde ich sagen, machen Sie es :-P
Patrick
es ist unschlagbar für Dinge wie ... | awk '{print $3}' | xargs | sed -e 's/ /+/g' | bc(um alle Werte von Feld 3 zu addieren). oder mit sed -e 's/ /|/g', um einen regulären Ausdruck zu erstellen. und ja, wie bei jedem Tool müssen Sie wissen, wie man es verwendet und welche Einschränkungen und Einschränkungen es hat.
Cas
Der vi $(...)Ansatz hat auch ein Problem mit Platzhaltern in anderen Shells als zsh.
Stéphane Chazelas
Beachten Sie auch, dass bei dem xargsAnsatz neben dem Leerzeichen-Problem auch Dateinamen mit einfachen Anführungszeichen, doppelten Anführungszeichen und umgekehrten Schrägstrichen ein Problem darstellen.
Stéphane Chazelas
10

Diese Frage wurde bereits im Super User- Forum gestellt.

Zitat aus der Antwort von @ grawity auf diese Frage:

Wenn Sie ein Programm über xargs aufrufen, zeigt das stdin (Standardeingabe) des Programms auf / dev / null. (Da xargs den ursprünglichen Standard nicht kennt, macht es das nächstbeste.)

Vim erwartet, dass sein Standard mit seinem steuernden Terminal identisch ist, und führt verschiedene terminalbezogene Ioctls direkt auf Standard aus. Wenn dies in / dev / null (oder einem anderen Nicht-Tty-Dateideskriptor) ausgeführt wird, sind diese Ioctls bedeutungslos und geben ENOTTY zurück, das stillschweigend ignoriert wird.

Dies wird in den Handbuchseiten für xarg erwähnt. Von OSX / BSD:

-o Öffnen Sie stdin als / dev / tty im untergeordneten Prozess erneut, bevor Sie den Befehl ausführen. Dies ist nützlich, wenn xargs eine interaktive Anwendung ausführen soll.

Daher können Sie unter OSX den folgenden Befehl verwenden:

find . -name "php.ini" | xargs -o vim

Während es in der GNU-Version keinen direkten Schalter gibt, funktioniert dieser Befehl. (Stellen Sie sicher, dass Sie die dummyZeichenfolge einschließen , da sonst die erste Datei gelöscht wird.)

find . -name "php.ini" | xargs bash -c '</dev/tty vim "$@"' dummy

Die oben genannten Lösungen wurden von Jaime McGuigan von SuperUser zur Verfügung gestellt . Fügen Sie sie hier für zukünftige Besucher hinzu, die die Site nach diesem Fehler durchsuchen.

verdammt
quelle
3
+1 danke für den -o Tipp. Ich benutze xargs seit Jahren und habe das nie bemerkt. Ich habe gerade die Manpage auf meinem System überprüft, weil es keine GNU xargs-Funktion ist. Die Manpage bietet xargs sh -c 'emacs "$@" < /dev/tty' emacseine flexiblere und tragbarere Alternative (obwohl es für GNU irgendwie lustig ist, die Portabilität den Funktionen vorzuziehen :).
Cas
2

Mit GNU findutilsund einer Shell mit Unterstützung für die Prozessersetzung (ksh, zsh, bash) können Sie Folgendes tun:

xargs -r0a <(locate -0 php.ini) vi

Die Idee ist, die Dateiliste -a filenameeher über ein als über ein stdin zu übergeben. Die Verwendung -0stellt sicher, dass es funktioniert, unabhängig davon, welche Zeichen oder Nichtzeichen die Dateinamen enthalten können.

Mit zshkönnten Sie tun:

vi ${(0)"$(locate -0 php.ini)"}

(Wo 0ist das Parametererweiterungsflag, das auf NULs aufgeteilt werden soll).

Beachten Sie jedoch, dass im Gegensatz dazu xargs -rimmer noch viohne Argument ausgeführt wird, wenn keine Datei gefunden wird.

Stéphane Chazelas
quelle
0

Mehrere php.ini im selben Editor bearbeiten?

Versuchen: vim -o $(locate php.ini)

Gänseblümchen
quelle
0

Dieser Fehler tritt auf, wenn vim aufgerufen wird und anstelle des Terminals mit dem Ausgang der vorherigen Pipeline verbunden ist und andere unerwartete Eingaben (wie NULs) empfängt. Das gleiche passiert , wenn Sie laufen: vim < /dev/null, so resetBefehl in diesem Fall hilft. Dies wird gut durch die Gravität bei Superuser erklärt .

Unter Unix / OSX können Sie verwenden , xargsmit -oParametern, wie zB:

locate php.ini | xargs -o vim

-oÖffnen Sie stdin als / dev / tty im untergeordneten Prozess erneut, bevor Sie den Befehl ausführen. Dies ist nützlich, wenn xargs eine interaktive Anwendung ausführen soll.

Versuchen Sie unter Linux die folgende Problemumgehung:

locate php.ini | xargs -J% sh -c 'vim < /dev/tty $@'

Verwenden Sie alternativ GNU , um die Zuweisung parallelvon xargstty zu erzwingen. Beispiel:

locate php.ini | parallel -X --tty vi

Hinweis: Unter parallelUnix / OSX funktioniert es nicht, da es unterschiedliche Parameter hat und tty nicht unterstützt.

Viele andere beliebte Befehle bieten ebenfalls eine Pseudo-Tty-Zuweisung (wie -tin ssh). Suchen Sie daher nach Hilfe.

Alternativ können Sie findDateinamen zum Bearbeiten übergeben. Sie müssen sie also nicht verwenden. xargsVerwenden Sie einfach Folgendes -exec:

find /etc -name php.ini -exec vim {} +
Kenorb
quelle
0

@ Patricks IFSHack ist nur für dumme Muscheln wie bashund notwendig zsh. fish teilt die Zeichenfolge standardmäßig in Zeilenumbrüche.

$ vim (locate php.ini)

Und Gott helfe uns allen, wenn einer von uns tatsächlich eine Datei mit einem Zeilenumbruch im Namen hat. Nach 17 Jahren mit Linux habe ich es noch kein einziges Mal gesehen. Ich würde mich nur darum kümmern, Dateinamen mit Zeilenumbrüchen für Skripte zu unterstützen, die auf jeden Fall funktionieren müssen, aber solche Skripte werden wahrscheinlich nicht interaktiv ausgeführt.

rätselhafter Physiker
quelle
zshwird standardmäßig auf SPC, TAB, NL und NUL aufgeteilt. Im Vergleich dazu bashwird das Ergebnis nicht globalisiert, sodass Platzhalterzeichen in Dateinamen kein Problem darstellen. In würden zshSie tun IFS=$'\0'; vi $(locate -0 php.ini)oder wie ich in meiner Antwort vi ${(0)"$(locate -0 php.ini)"}für einen expliziten Aufteilungsoperator gezeigt habe. Beachten Sie auch tcsh'svi "`locate php.ini`"
Stéphane Chazelas
Oh verdammt. OK, das funktioniert: $ f='not there'<ret>$ ls $f<ret>aber das funktioniert nicht: ls echo not there. OK sieht so aus, als müsste ich das ein bisschen aktualisieren.
rätselhafter
Ja, zsh macht nicht das Richtige, wenn du es tust ls "$(echo test; echo other test)". Nur Fisch macht das Richtige.
EnigmaticPhysicist
Angenommen, Sie meinten dasselbe ohne die Anführungszeichen, das ist nicht "richtig", das teilt sich in Zeilen auf, es ist nur eine andere Wahl. zsh teilt standardmäßig Wörter (wie alle anderen Shells) und kann angewiesen werden, auf Zeilen oder NULs entweder über $IFSoder über explizite Operatoren ( fund 0Parametererweiterungsflags) zu teilen . Bei beliebigen Dateinamen ist das Aufteilen nach Wörtern oder nach Zeilen gleichermaßen falsch . Sie müssen auf NUL aufteilen oder eine Codierung analysieren, was fishnicht möglich ist. In zsh, das ist IFS=$'\0'; ls -ld -- $(printf '%s\0' "$file1" "$file2")oderls -ld -- ${(0)"$(printf '%s\0' "$file1" "$file2")"}
Stéphane Chazelas
Meh. Das Aufteilen auf Zeilenumbrüche ist gut genug. Wie die Antwort sagt, sind Zeilenumbrüche in Dateinamen äußerst selten. Ich habe es in 17 Jahren buchstäblich nie gesehen. Und Zeilenumbrüche sind weitaus bequemere Trennzeichen als Nullen.
EnigmaticPhysicist
0

Ein schneller Weg , es zu tun, vorausgesetzt , Sie keine der Dateipfade garantieren können , enthalten SPC, TAB, NL, *, ?, [Zeichen (auch \und {...}in einigen Muscheln) ist zurück-Zecken zu verwenden (auch bekannt als Grab Akzente) , einen Befehl auszuführen vor Ein weiterer Befehl wird ausgeführt.

Z.B

vi `find / -type f -name 'php.ini'`

Der in den Back-Ticks enthaltene Befehl wird zuerst ausgeführt. Die Ausgabe des enthaltenen Befehls wird dann durch den vor den Back-Ticks angegebenen Befehl ausgeführt.

In der obigen Zeile wird der find / -type f -name 'php.ini'Befehl beispielsweise zuerst ausgeführt, die Ausgabe vigesendet und dann für das Ergebnis von split + glob ausgeführt, das auf diese Ausgabe angewendet wird.

Tacotuesday
quelle
3
Back-Ticks sind für einfache Anführungszeichen zu leicht zu verwechseln. Verwenden Sie $(find ...)stattdessen.
Cas
1
Vermutlich wird dies auch bei Leerzeichen und / oder Zeilenumbrüchen in den Dateinamen unterbrochen?
CWD
So führen Sie Shell-Befehle in Bash-Skripten aus. Ich hatte noch nie einen Bruch an Leerzeichen oder neuen Zeilen in meinen Skripten oder bei der Verwendung in einem Einzeiler. Ich habe jedoch nie versucht, mehrere Dateien in zu öffnenvi mit dieser Methode . Es ist durchaus möglich, dass neue Zeilen oder Leerzeichen unterbrochen werden, je nachdem, wie vidie Ausgabe gelesen und ausgeführt wird.
Tacotuesday