3

I've been using bash's edit-and-execute-command function:

edit-and-execute-command (C-x C-e)

Invoke an editor on the current command line, and execute the result as shell commands. Bash attempts to invoke $VISUAL, $EDITOR, and emacs as the editor, in that order.

https://www.gnu.org/software/bash/manual/html_node/Miscellaneous-Commands.html

I've noticed that if I invoke an editor, use Ctrl-Z to put it into the background, and then use fg to put it back into the foreground, the shell no longer executes the temporary file.

This is handy if I want to abort the command, but I found the behavior a little surprising the first time it happened.

My questions:

  • Why does this happen?

    I know from the source code that edit_and_execute_command eventually calls fc, but it's not immediately clear to me why sending SIGTSTP prevents bash from executing the temporary file.

  • If I had accidentally hit Ctrl-Z and still wanted to execute the script in the temporary file still open by the editor, what would be the best way of doing that?

2 Answers 2

2
+50

If I had accidentally hit Ctrl-Z and still wanted to execute the script in the temporary file still open by the editor, what would be the best way of doing that?

You could try using and editor wrapper which would save the last command somewhere, and a readline binding which could bring it back. Example which saves the last buffer to ~/.last_fcedit and binds Esc-/ (Alt-/) to replace the command line with it:

LAST_FCEDIT=$HOME/.last_fcedit
fc(){ case $1 in -e) _fce=$2; set -- "$@" -e _fcedit; esac; command fc "$@"; }
_fcedit()("$_fce" "$@"; s=$?; test -f "$1" && mv "$1" "$LAST_FCEDIT"; exit "$s")
bind -x '"\e/":READLINE_LINE=$(<"$LAST_FCEDIT")'

Then

$ echo FOO<Ctrl-X Ctrl-E>
... in editor ...
<Ctrl-Z>
[1]+  Stopped                 ( "$_fce" "$@"; s=$?; mv "$1" "$LAST_FCEDIT"; exit "$s" )
$ fg<Enter>
... edit FOO to BAR and exit the editor ...
( "$_fce" "$@"; s=$?; mv "$1" "$LAST_FCEDIT"; exit "$s" )
$ <Alt-/>echo BAR<Enter>
BAR

Why does this happen?

I'm not able to find a convincing rationale for it, but it's the same with plain fc (and related keybindings) on all the shell implementations I could check.

My idea is that it has do with the shell not being able to recursively background or foreground itself, but only its child jobs (process groups). The same thing you could notice with:

$ foo(){ cat; echo DONE; }
$ foo
^Z
[1]+  Stopped                 cat
DONE  # but cat is still running!
$ fg
cat
^D
$

or

$ foo=$(cat)
^Z^Z^Z^Z^Z^Z # no way to suspend it!

Unlike in bash, the first example will actually "work" in ksh93 or zsh; but the second will be unkillable in ksh93 or zsh ;-)

0

Ctrl-Z triggers SIGTSTP, see https://stackoverflow.com/questions/11886812/whats-the-difference-between-sigstop-and-sigtstp/11888074 . SIGTSTP suspends the process :

Factoid: If you're a linux programmer, SIGTSTP is what you get when you use Ctrl-Z to interrupt a process running in a shell without killing it. This usually causes the shell to put it on a suspended job list.

simpleusers's comment under first answer in https://stackoverflow.com/questions/11886812/whats-the-difference-between-sigstop-and-sigtstp/11888074

Why SIGTSTP suspends a process is because this is defined in the kernel, SIGTSTPsuspends a process (http://man7.org/linux/man-pages/man7/signal.7.html)

5

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .