File descriptors are inherited
What you're using is
<<< aka here-string, and that type of redirection is implemented via temporary files that are immediately unlinked (aka deleted). Knowing also that processes inherit file descriptors (and stdin among them)1 from parent to child, when
sudo -S is performed, it reads its input from the temporary file that your original shell has passed to it (the one that reads
sudo -S command). You can see that in action:
$ sudo -S bash -c 'ls -l /proc/self/fd/0' <<< "mypasswd"
lr-x------ 1 root root 64 Feb 26 13:12 /proc/self/fd/0 -> '/tmp/sh-thd.cUOOXU (deleted)'
Too many errors from stdin error in such case. What can be done is to manually re-wire where
stdin is coming from.
$ sudo -S bash -c 'exec < /dev/tty; nano /tmp/foobar' <<< "mypasswd"
$ sudo -S bash -c 'nano /tmp/foobar < /dev/tty' <<< "mypasswd"
Either of the two commands will lauch
nano properly and other commands as well.
/dev/tty is the special file representing current terminal device, and it is effectively the same thing as
stdin of "normal" interactive shell (and of course I am oversimplifying that statement).
Another variation on the theme is
$ sudo -S bash -c 'nano /tmp/foobar 0>&1' <<<"mypasswd".
stdout effectively points to the same terminal device (unless you've explicitly provided a redirection to have stdout point somewhere else). Thus, if we make
stdin point to be the
stdout, it's effectively the same solution as above. (And if you're wondering why I highlighted
dup part, that's because
dup2() syscall is the one responsible for all the juggling of file descriptors under the hood)
However, note that I don't encourage using
sudo -S in interactive shell, since passwords will remain in shell's history
$ sudo -S bash -c 'sleep 3m' <<< "ohnomypassword"
$ history 2
2016 sudo -S bash -c 'sleep 3m' <<< "ohnomypassword"
2017 history 2
sudo -S is more appropriate for use with other processes, where
sudo -S would expect input via pipeline, ideally from
zenity --password or
dialog --passwordbox "foobar text" 200 200 (see extra1 and extra2 about dialog).
1. If file is open with
O_CLOEXEC flag set, the child process or replaced process spawned via
execve() type of flag won't have access to that file descriptor - that is it will be closed on