The Computer Oracle

Invoking vi through find | xargs breaks my terminal. Why?

Become part of the top 3% of the developers by applying to Toptal https://topt.al/25cXVn

--

Track title: CC P Beethoven - Piano Sonata No 2 in A

--

Chapters
00:00 Question
00:43 Accepted answer (Score 115)
02:23 Answer 2 (Score 184)
03:30 Answer 3 (Score 34)
03:42 Answer 4 (Score 24)
03:59 Thank you

--

Full question
https://superuser.com/questions/336016/i...

Question links:
[elsewhere]: http://superuser.com/questions/335999

Accepted answer links:
[ioctl]: http://en.wikipedia.org/wiki/Ioctl

Answer 2 links:
[man xargs]: http://man7.org/linux/man-pages/man1/xar...

--

Content licensed under CC BY-SA
https://meta.stackexchange.com/help/lice...

--

Tags
#vim #find

#avk47



ANSWER 1

Score 186


(Following on from grawity's explanation, that xargs points stdin to /dev/null.)

The solution for this problem is to add the -o parameter to xargs.  From man xargs:

-o

      Reopen stdin as /dev/tty in the child process before executing the command.  This is useful if you want xargs to run an interactive application.

Thus, the following line of code should work for you:

find . -name "*.txt" | xargs -o vim

GNU xargs supports this extension since some release in 2017 (with the long option name --open-tty).

For older or other versions of xargs, you can explicitly pass in /dev/tty to solve the problem:

find . -name "*.txt" | xargs bash -c '</dev/tty vim "$@"' ignoreme

(The ignoreme is there to take up $0, so that $@ is all arguments from xargs.)




ACCEPTED ANSWER

Score 117


When you invoke a program via xargs, the program's stdin (standard input) points to /dev/null. (Since xargs doesn't know the original stdin, it does the next best thing.)

$ true | xargs filan -s
    0 chrdev /dev/null
    1 tty /dev/pts/1
    2 tty /dev/pts/1

$ true | xargs ls -l /dev/fd/

Vim expects its stdin to be the same as its controlling terminal, and performs various terminal-related ioctl's on stdin directly. When done on /dev/null (or any non-tty file descriptor), those ioctls are meaningless and return ENOTTY, which gets silently ignored.

  • My guess at a more specific cause: On startup Vim reads and remembers the old terminal settings, and restores them back when exiting. In our situation, when the "old settings" are requested for a non-tty fd (file descriptor), Vim receives all values empty and all options disabled, and carelessly sets the same to your terminal.

    You can see this by running vim < /dev/null, exiting it, then running stty, which will output a whole lot of <undef>s. On Linux, running stty sane will make the terminal usable again (although it will have lost such options as iutf8, possibly causing minor annoyances later).

You could consider this a bug in Vim, since it can open /dev/tty for terminal control, but doesn't. (At some point during startup, Vim duplicates its stderr to stdin, which allows it to read your input commands – from a fd opened for writing – but even that is not done early enough.)




ANSWER 3

Score 24


It should work just fine if you use the -exec option on find rather than piping into xargs.

find . -type f -name filename.txt -exec vi {} + 



ANSWER 4

Score 11


Use GNU Parallel instead:

find . -name "*.txt" | parallel -j1 --tty vim

Or if you want to open all the files in one go:

find . -name "*.txt" | parallel -Xj1 --tty vim

It even deals correctly with filenames like:

My brother's 12" records.txt

Watch the intro video to learn more: http://www.youtube.com/watch?v=OpaiGYxkSuQ