tl;dr
strace -o '|vim -' file.out
About -o
-o
exists to provide additional output channel different than stdout or stderr. Without -o
strace
prints to its stderr which is also the stderr of the command (file.out
in your case). It's good to be able to separate the two streams, -o
makes this possible.
Problems
Your approach is flawed in a different way than you think, stty sane
cannot help. Let me repeat the command:
strace -o >(vim -; stty sane) file.out; stty sane
There are two general problems with it:
- With job control enabled, there's a concept of foreground process group. At any time the terminal recognizes one group that is in the foreground. Processes not in the foreground process group are formally in the background.
A process in the background cannot read from its controlling terminal; it will receive SIGTTIN
if it tries. The signal will stop it, unless the process ignores it or handles differently.
A process in the background can write to its controlling terminal, although if it tries and stty tostop
is enabled then the process will receive SIGTTOU
. The signal will stop it, unless the process ignores it or handles differently. If not stopped, the process is able to write to the terminal despite tostop
and the signal.
The above mechanism prevents background processes from stealing input from the terminal, but still keeps them connected to it, so they can be brought to the foreground and then read from the terminal. This is useful for the terminal from which a process was started. Possible interaction with other terminals has nothing to do with being in the foreground or in the background, the other terminal is "foreign" anyway; therefore a process can read from or write to a terminal that is not its controlling terminal, if only it has sufficient permissions.
In your case initially the foreground process group is the one associated with the shell; then with strace
; then with stty
(the one outside >()
); and finally with the shell again.
In Bash processes inside >()
are assigned to the process group of the shell handling the >()
. This means your vim
is able to read from the terminal only after strace
and stty
(the one outside >()
) finish and the shell's process group is put in the foreground. This brings the second problem.
- When
vim
is finally able to read from the terminal, the shell also tries to read. Compare these two:
strace -o >(vim -) true
Move the cursor around. Some input will go to vim
, some input will go to the shell. They are both in the foreground.
strace -o >(vim -) true; sleep 1000
Try to move the cursor around. Now it's different because sleep
is in the foreground. Terminate sleep
with Ctrl+C and the behavior will change.
In any case you can recover by killing vim
from another console (killall vim
, unless there is another vim
and you want to keep it).
This is the madness you observed. The culprit is beyond the scope of stty sane
, reset
or tput reset
.
Trivia
The following commands generate non-identical madness:
<<<"foo" tee >(vim -)
<<<"foo" tee > >(vim -)
In the first case >()
is handled by the main shell and vim
is placed in the process group of the shell. It will be able to read from the terminal as soon as tee
exits (although the shell interferes); we've seen this before with strace
.
In the second case >()
is handled by the shell that handles >
. It's a subshell that is going to become (exec to) tee
. In effect vim
is placed in a process group with tee
. It is able to read from the terminal as long as tee
stays in the foreground.
Because tee
exits almost immediately, the first vim
is almost immediately able to read from the terminal and the second vim
is almost immediately unable to read.
- My tests indicate that
>(vim -)
in Zsh places vim
in yet another process group, not the one of the shell handling the >()
. Then vim
can never be in the foreground. It seems there are other differences but I won't elaborate.
General solutions (not depending on strace
)
If you disable job control (with set +m
or by running code in a subshell) then all new processes will belong to the group the terminal considers the foreground. If you delay your return to the shell, the shell won't interfere by reading from the terminal. Try this:
(strace -o >(vim -) file.out; sleep 99999)
Use Ctrl+C to terminate sleep
after you exit vim
. If you hit this combination in vim
then sleep
won't be affected because vim
configures the terminal like stty -isig
.
This solution is not really elegant. I'm including it here because it shows how things are affected by disabling job control and not allowing the shell to read input.
- Alternatively you need to start and keep
vim
in the foreground. Do not use >(vim -)
. Use vim …
or … | vim -
.
The most straightforward way is to create a regular file (in your case strace -o somefile file.out
) and to open it later (vim somefile
).
Solution specific to strace
Solutions that avoid creating a file and use vim <(strace …)
or strace … | vim -
are possible. I guess if you are clever enough (and have /proc
?) then you can manipulate file descriptors and pipe -o
to stdout without merging with the original stdout.
The right thing however is to use what strace
provides with -o
:
-o filename
--output=filename
Write the trace output to the file filename
rather than to stderr. […] If the argument begins with |
or !
, the rest of the argument is treated as a command and all output is piped to it. This is convenient for piping the debugging output to a program without affecting the redirections of executed programs. […]
(source, emphasis mine)
Your command becomes:
strace -o '|vim -' file.out
Note |
must be quoted or escaped, otherwise the shell will try to build a pipeline. The alternative is !
, in some shells it needs to be protected as well.
strace
runs sh
, sh
runs vim
. The problems stated above are solved. Respectively:
The three processes belong to the same process group. If strace
is in the foreground, so is vim
.
The interactive (outer) shell tries to read from the terminal only after strace
exits after sh
exits after vim
exits.
stdin
? The point is to only sets back the environment variable, becausevim
sets them to null when input is not fromstdin
(hence the strange behaviour inside vim), and therefor I am trying tostty sane
but to no avail – Herdsman May 23 '20 at 15:09tee
does not help :strace a.out | tee 1>/dev/null >(vim -)
will not simply inputtee
in vim – Herdsman May 23 '20 at 15:28$ls -l <(echo abc)
->lr-x------ 1 group user 64 May 23 20:49 /dev/fd/63 -> 'pipe:[33372]'
. Though that is not solution to my question – Herdsman May 23 '20 at 18:52