45

I was wondering if there was a quick way to save a command in Ubuntu's terminal. The scenario is:

Problem:

  1. Typed out a [long command]
  2. Forgot I needed to run [another command] before I could run the [long command]

I want to be able to save the command for later use in an easy way that's not just putting a # before it and putting it in the up and down key history. Optimally saving it directly to a register or the clipboard.

I forgot to mention that I didn't want to echo, either.

  • 46
    Why not using # in front of the command? – Kusalananda Feb 27 at 13:16
  • With bash in Ubuntu, I can hit Meta+Enter (=Alt+Enter) which places the # in front of the command and give a newline in one go, without moving the cursor, just a half second faster than POS1 (HOME)-key for movement. Of course, you need to try it out with some simple, harmless commands. – user unknown Feb 29 at 18:49
  • What keyboard nomenclature are you using? @userunknown – Preston Feb 29 at 18:54
  • 3
    @userunknown The standard shortcut is alt + # which is referred to as the 'insert-comment' command in the Bash docs (their notation is M-#). gnu.org/software/bash/manual/bash.html#Command-Line-Editing – Max Barraclough Feb 29 at 19:27
  • @Preston: Yes, sorry, Alt+#, not Alt+Enter, like MaxBarraclough said. – user unknown Feb 29 at 19:49

10 Answers 10

52

This is not your terminal, this is your shell.

The name for the shell mechanism that you are looking for is a kill buffer. People forget that shell command line editors have these. ZLE in the Z shell has them, as have GNU Readline in the Bourne Again shell, libedit in the (FreeBSD) Almquist shell, the Korn shell's line editor, and the TENEX C shell's line editor.

In all of these shells in emacs mode, simply go to the end of the line to be saved, kill it to the head kill buffer with ⎈ Control+U, type and run the intermediate command, and then yank the kill buffer contents with ⎈ Control+Y. Ensure that you do not do anything with the kill buffer when entering the intermediate command.

In the Z shell in vi mode, you have the vi prefix sequences for specifying a named vi-style buffer to kill the line into. You can use one of the other buffers instead of the default buffer. Simply use something like " a d d (in vicmd mode) to delete the whole line into buffer "a", type and run the intermediate command, and then put that buffer's contents with " a p.

In their vi modes, the Korn shell, GNU Readline in the Bourne Again shell, and libedit in the (FreeBSD) Almquist shell do not have named vi-style buffers, only the one cut buffer. d d to delete the line into that buffer, followed by putting the buffer contents with p, will work. But it uses the same vi-style buffer that killing and yanking will while entering the intermediate command.

31

You don't want to prepend echo (or any other no-op-type command) to your line, since you may have I/O redirection, and they would still be executed, potentially overwriting any file you might need to still access (until you are ready to execute your command).

Instead, go to the beginning of the line and enter the comment character, #. Then you can press Enter, and the line will be saved in your history.

If you are in vi mode, ksh and bash (at least) have a specific command for this: enter command mode (press Esc) then press the # character.

This is specifically for this use-case. The description of this command in the O'Reilly Learning the Korn Shell book says:

Prepend # (comment character) to the line and send it to the history file; useful for saving a command to be executed later without having to retype it. If the line already starts with a #, remove the leading # and any other comment characters that follow newlines in a multiline command.

  • 6
    The downside is that this is shell-specific. The Z shell with extended patterns turned on, for example, treats this as a non-matching pattern, rather than a comment. Plus, of course, the question specifically asked for not this. – JdeBP Feb 27 at 14:17
  • 1
    @JdeBP With thi interactive_comments option set in an interactive zsh shell, # will be a comment character. This shell option is set by default in bash. – Kusalananda Feb 27 at 18:57
  • 3
    It isn't by default in the Z shell. and this is still what the questioner specifically excluded. – JdeBP Feb 27 at 21:33
16

If you are running zsh, you can use Ctrl-Q, which runs the command push-line. It will save what you have currently typed onto a stack, clear the prompt, and then pop the stack after you enter your next command. I use this all the time for exactly the situation you describe.

  • 1
    This is another option, indeed. An interesting question, not answered by the Z shell doco, is how exactly this "buffer stack" relates to the numbered buffers in vi mode. – JdeBP Feb 28 at 14:51
6

I usually just append "'" (single quote) at the end of the line and press Enter and Ctrl-C. Later I recall it with Cursor Up or Ctrl-R. You have to be sure that there are no stray quotes earlier, though.

  • 3
    I use a similiar trick with a \ (backslash) then Enter. It goes to the next line and then I can Ctrl+C safely. – cadesalaberry Feb 27 at 17:03
4

If your shell is set to emacs mode (usual default for bash, I believe):

#set -o | grep emacs
emacs         on

then you can:

<Ctrl>a or <arrowkeys> # to go to the beginning of the current line or to wherever you want
<Ctrl>k    # to "kill" the content from the cursor position to the end of the line + SAVE it
... here you type the command you forgot to add ...
<Ctrl>y    # to "yank" the SAVED line into the current editing line. or <Ctrl>p in ksh(??)
<Enter>    # to execute it

#note: you can also replace: <Ctrl>a<Ctrl>k with: <Ctrl>u # kills the whole line + SAVE it

This is very quick to do, and becomes a habit. ctrl-k can also be usefull to only save the "end" of a line (for example: 2 long arguments that you know you will reuse later, that you "kill"(+save), and that you can then "yank" back as often as you want)

If your shell is in vi mode, you can use the usual vim commands:

<Esc>      # to go back to normal(command) mode, if you were in editing mode
dd         # to delete the whole current line + SAVE it  (see note below)
i          # to reenter editing mode
... here you type the command(s) you forgot ...
<Esc>      # to return to normal mode
p          # to paste the buffered line + place the cursor on top of the last character
a          # to reenter editing mode past that current character
<Enter>    # to execute it

# note: instead of "dd" (kill whole line) you can simulate emacs <Ctrl>a<Ctrl>k with:
#    | or h or l  # to go to the 1st char of the line or wherever # simulates <Ctrl>a or <arrow keys>
#    d$  # to delete from the current position to the end of the line + SAVE it # <Ctrl>k
4

Workaround that might work depending on case.

really long command thigng -flag -flag2

Press home, enter new command and then && to run first, and if successful run second.

first command && really long command thigng -flag -flag2

How ever will then not be saved as separate steps.

Alternatively:

If you already entered the first command (as in pressed enter) you can type first_command && !! which will insert the second command in !!

  • Or ; instead of && if you want the 2nd command to run regardless of the success of the 1st. – Tanath Mar 4 at 17:34
3

You can select/copy the text either with the GUI or the shell's facilities, and then either paste it into some file or just not forget to not overwrite it by a subsequent select (so you can paste it back when needed), but I find that awkward: the place of full command lines is IMHO in the "up and down key history".

Instead of adding a # at the beginning of the line and pressing <Enter>, you can create a keybinding which saves the current command line in the history without executing it.

Eg. with bash, binding it to Meta/Alt-C or Esc-C (usually bound to capitalize-word in the emacs keybindings; use another key if you're already using it):

bind -x '"\ec": history -s "$READLINE_LINE"; READLINE_LINE='

If you're using the vi-keybindings, you can squat the unused ^G for it:

bind -x '"\C-g": history -s "$READLINE_LINE"; READLINE_LINE='

That would also clear the line afterwards; if you don't want that (and prefer to manually cancel the line with eg. ^C), omit the READLINE_LINE=.

  • With clipboard history from apps like Clipman it's unlikely you'd need to save it elsewhere in a file. – Tanath Mar 4 at 17:36
  • I use my own clipboard history thing, but I would not use it in this case, because I think that the place for such "left for later" commands is in the command history -- I may connect from another place to the same machine and I want to retrieve it there. – mosvy Mar 4 at 18:25
  • I think it only belongs in the command history if you run the command. The point here is you haven't, but want to shortly. Clipboard history is perfect for that. It's unlikely you're switching to remote access in between the 2 commands, but that's where you'd make it accessible. – Tanath Mar 4 at 18:32
2

If you have already typed the second command, go to the beginning of the line, type the first command, and then a semicolon to separate the two. This is if success doesn’t matter. If it does matter, the && someone else suggested.

2

Rather than deal with various, possibly esoteric readline keyboard shortcuts, you can edit your shell's history file directly. With bash:

  1. history -w (Flush existing history)
  2. $EDITOR $HISTFILE (Edit your history file (~/.bash_history by default) and make whatever changes you want)
  3. history -c ; history -r (Clear your shell's in-memory history and reload from the history file)

Since I like to make adjustments to my bash history a lot, I wrote a bash function to make it more convenient:

histedit()
{
  # Flush history.
  history -w

  # Open an editor at the last line.  Pipe the file into `wc` to suppress
  # printing the filename.
  #
  # Note that some versions of `wc` (e.g. BSD) do not support long options
  # and print with leading spaces.
  last_line=$(wc -l < "${HISTFILE}")
  last_line=$(( "${last_line}" ))

  "${EDITOR}" "+${last_line}" "${HISTFILE}"

  # Reload the history file.
  history -c
  history -r
}

I additionally have a separate bash function to perform a quick search and replace on my bash history to correct and re-run typoed commands.

1

No, it will not be saved into the commandline history because that only takes commands that have been entered.

You can, however prepend "echo" to save something that was complicated to type

echo /usr/bin/complicate -v e -r y -c o -m p -l ic -a t -t ed

Then recall the echo command and edit to remove "echo ".

If you need something in the history as a preparation for later you could also edit the history file, e.g. ~/.bash_history for the bash or ~/.history for tcsh. Edit by changing an existing command, that is easiest because you do not need to try to guess the file syntax. Next time the shell start if will be read in (if the shell is configured to save and read history, which happens by default on most systems).

A third way to use a complicated command without a lot of typing would be to define an alias for the command. Again the syntax varies by shell variant:

bash:

alias ll='ls -algF --color=auto'

tcsh:

alias ll 'ls -algF --color=auto'

The new command ll will do the same as entering ls -algF --color=auto. You can see existing aliases (for examples) by simply executing alias without parameters.

These lines can be entered into ~/.bashrc and ~/.tcshrc, repectively to save them for all coming shell startups.

  • 4
    IMHO instead of echo you can just use #. It's shorter and ensure that nothing will be done on that line (if your command contains subshells using echo they will run anyway... which may be useful to see which command would have been run but if they have side-effects you may not want to run them). – Giacomo Alzetta Feb 27 at 9:00
  • 2
    The question specifically asked for not that. – JdeBP Feb 27 at 14:20
  • @GiacomoAlzetta That does not work in all shells. Mine says #: Command not found.. That's why I use echo. – Ned64 Feb 27 at 20:30
  • @JdeBP You are right. That wasn't clear to me at the beginning (possibly because the question was edited). I will leave my reply for now because it has other options than just the #. Upvoted your Answer, of course because you got it. – Ned64 Feb 27 at 20:31
  • I was actually addressing the immediately preceding comment critiquing your answer, suggesting as an improvement something that was specifically excluded by the questioner. – JdeBP Feb 27 at 21:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.