xargs and vi - "Input is not from a terminal"
This question has previously been asked on the Super User forum.
Quoting from @grawity's answer on that question:
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.)
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.
This is mentioned in the manual pages for xarg. From OSX/BSD:
-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.
Hence, on OSX, you could use the following command:
find . -name "php.ini" | xargs -o vim
While, there is no direct switch on the GNU version, this command will work. (Make sure to include the dummy
string, otherwise it will drop the first file.)
find . -name "php.ini" | xargs bash -c '</dev/tty vim "$@"' dummy
The above solutions are courtesy Jaime McGuigan on SuperUser. Adding them here for any future visitors searching the site for this error.
vi $(locate php.ini)
Note: this will have issues if your file paths have spaces, but it is functionally equivalent to your command.
This next version will properly handle spaces but is a bit more complicated (newlines in file names will still break it though)
(IFS=$'\n'; vi $(locate php.ini))
Explanation:
What's happening is that programs inherit their file descriptors from the process that spawned them. xargs
has its STDIN connected to the STDOUT of locate
, so vi
has no clue what the original STDIN really in.
With GNU findutils
, and a shell with support for process substitution (ksh, zsh, bash), you can do:
xargs -r0a <(locate -0 php.ini) vi
The idea being to pass the file list via a -a filename
rather than stdin. Using -0
makes sure it works regardless of what characters or non-characters the file names may contain.
With zsh
, you could do:
vi ${(0)"$(locate -0 php.ini)"}
(where 0
is the parameter expansion flag to split on NULs).
However note that contrary to xargs -r
that still runs vi
without argument if no file is found.