1

this makes vim madness:

$strace -o >(vim -; stty sane) file.out; stty sane

I have typed stty sane in processed substitution as well as in next command but none of them did their job. Once i piped the strace command to vim, then the vim no longer behaves normaly (I know vim can only accepts stdin and strace gives output to stderr, but then what is the -o flag for?) any help?

Herdsman
  • 340
  • Doesn't look very interesting to have a text editor working on a pipe. – A.B May 23 '20 at 15:04
  • Why not? I just take advantage of pipe/redirection/process substitution to get some data from one process to another. Why could not vim accept input from ohter source then stdin? The point is to only sets back the environment variable, because vim sets them to null when input is not from stdin (hence the strange behaviour inside vim), and therefor I am trying to stty sane but to no avail – Herdsman May 23 '20 at 15:09
  • Use tee ! something like: 'strace some-program|tee /tmp/strace.out |less' – Stefan Skoglund May 23 '20 at 15:23
  • Do you know what strace/truss is used for ? – Stefan Skoglund May 23 '20 at 15:24
  • @StefanSkoglund, yes I do know. tee does not help : strace a.out | tee 1>/dev/null >(vim -) will not simply input tee in vim – Herdsman May 23 '20 at 15:28
  • Let vim read strace.out while you monitor the constant output from strace. Though a number of users has asked about how to replace less (or more) with vim. It is possible to do so. strace's option '-o' is how to name a file, not a pipe ! – Stefan Skoglund May 23 '20 at 17:30
  • @StefanSkoglund well, technically, process substitution IS special file (symbolic link in this case): $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
  • Also see: moreutils (search for: vipe). – Vilinkameni Mar 29 '24 at 06:36

1 Answers1

3

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:

  1. 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.

  1. 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

  1. 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.

  1. 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:

  1. The three processes belong to the same process group. If strace is in the foreground, so is vim.

  2. The interactive (outer) shell tries to read from the terminal only after strace exits after sh exits after vim exits.

  • The order - why is >() handled AFTER every command separate by semicolon ; (command assigned to shell group directly)? Should I assumed >() to be subshell, and therefor be executed delayed (and thus after main shell)?
  • – Herdsman May 24 '20 at 11:37
  • what do you mean by subshell will become tee - the tee commans is triggered from Main shell (because it is placed before > redirection/subshell) so would not be in redirection/subshell but rather in main shell (initiall) from my understanding. A little explanation would be appreciated
  • – Herdsman May 24 '20 at 11:43
  • last question 3) why is even >() treated NOT in foreground group? Should I assume that everything that is not separated by ; (e.g. pipe |, redirection >, background & or already mention process substitution >()) is in different process group from the initial main shell? And the consequence is only the order (mentioned in my first question) of the comnands? Thanks for answering all questions – Herdsman May 24 '20 at 11:50
  • @Herdsman (1) I don't understand the question. An example where >() is allegedly delayed may help. (2) How a shell runs an executable: the main shell forks; the child shell prepares its own environment and redirections; the child shell replaces itself with the executable. If you run pstree > >(cat) in Bash then you will see bash - pstree - bash - cat. It doesn't mean pstree is able to run bash. It used to be bash - bash - bash. Respectively: interactive main shell; a child shell about to exec to pstree; a shell about to run the inside of >(). (cont'd) – Kamil Maciorowski May 24 '20 at 12:09
  • @Herdsman (cont'd) The third bash runs cat in a similar way: yet another (the fourth) bash is created, then it execs to cat. I didn't write bash - bash - bash - bash because maybe bash - pstree - bash was before bash - pstree - bash - bash and bash - bash - bash - bash never existed, I don't know. The point is pstree used to be bash and cat used to be bash. – Kamil Maciorowski May 24 '20 at 12:14
  • @Herdsman (3) If job control is enabled, when running foo or foo | bar | … Bash places every process in a process group with PGID (process group ID) equal to the PID of the first command (foo). If the command is run in the foreground, the terminal is informed this PGID should now be considered the foreground process group. The main shell itself is not in this process group; it's formally in the background. >() is handled by the main shell and everything spawned from it is not in the process group of foo, hence not in the foreground. Well usually; remember the example with > >(). – Kamil Maciorowski May 24 '20 at 12:37
  • Macorowski, your statemnet: The point is pstree used to be bash and cat used to be bash means in other word, that every executable (process to be run) has its own bash that runs it? If so, then 1)rational behind it (why?) and 2) is not it too overwhelm with processes, because before every process is a instance of bash? (Can it cause leaks of cpu time)? – Herdsman May 25 '20 at 17:43
  • @Herdsman Not every process but basically every process started by bash. This is a common procedure. – Kamil Maciorowski May 25 '20 at 18:00