22

I'm typing that command and my cursor is at the end of "supprimer des warnings"

$ git commit -m "Nettoyage :
>     - Suppression de sources ou projets inutiles
>     - Corrections mineures sur les sources pour supprimer des warnings"

It's the time I notice that I should have written "Nettoyage (deuxième partie)" at the beginning of my commit message.

...but how, being at the last line of my command, may I go up to the beginning of it, to edit it, on its first line?

7
  • 17
    This isn't an answer to your question, but if this is specific to git commit messages, you can omit -m <message>, and git will open a text editor and allow you to type your message there, committing when you save and exit. The editor it uses is the GITEDITOR env var, which can be set in a .bashrc. I set export GITEDITOR=nvim because it is easier than trying to deal with bash's multi-line strings. Commented Mar 21, 2022 at 20:10
  • 12
    And also specific to git commits, you can always amend the message later using git commit --amend
    – muru
    Commented Mar 22, 2022 at 1:09
  • 3
    @phuclv isn't powershell a Microsoft tool that transforms MS-DOS command lines in something approaching more bash commands? I don't see its goals in Linux environments. Commented Mar 22, 2022 at 6:48
  • 1
    @Marc how is MS-DOS related here? Not even bash. The fact that it was developed by MS has no relevance either. PowerShell is just another new shell, with the syntax has some influence from ksh, not bash. And it's a shell so it can run anywhere, and it's extremely powerful. Here's an example
    – phuclv
    Commented Mar 22, 2022 at 8:30
  • 10
    @phuclv "It's a shell so it can run anywhere" is nonsense; there's nothing magic about shells that makes them cross-platform, it only runs "anywhere" because Microsoft have specifically (re-)engineered it to be cross-platform. PowerShell was explicitly designed as a CLI for Windows, and only ran on Windows for the first 10 years of its existence. That legacy has definite consequences, since it's designed to rely heavily on .Net libraries and its own built-in primitives for things that most shells designed for *nix would get from standard system commands.
    – IMSoP
    Commented Mar 22, 2022 at 14:10

5 Answers 5

54

Unfortunately, command entry in Bash is line-oriented, and you can’t go back to a previous line while entering a multi-line command.

What you can do however is start an editor with the full command entered so far. To do so, in Emacs mode (the default), press CtrlxCtrle; in vi mode, press Escv. This will open your editor with everything you’ve entered so far; fix what needs fixing, complete the command, exit the editor and Bash will run the edited command.

In this particular case you could use an editor for the entire git commit message: omit the -m option and git will start an editor for you.

5
  • 12
    I have been using bash for decades and I never knew it could do this! Commented Mar 22, 2022 at 19:23
  • 4
    When I test this, it lets me fix the command in my editor, then it runs the fixed command, but it also leaves me still editing the original partial command (with any output showing up in the middle), so I have to cancel it with Ctrl-c. Is that normal? Commented Mar 22, 2022 at 19:38
  • 1
    in vi mode you just hit ESC + v, fwiw
    – Wes
    Commented Mar 23, 2022 at 19:54
  • I never knew that. Is there a secret key combination to enter nano mode ? (I can't stand either vi or emacs, burn me at the stakes)
    – dargaud
    Commented Mar 24, 2022 at 8:58
  • 2
    @dargaud vi and emacs mode only affect the keyboard shortcuts. Bash uses your EDITOR variable to open your text editor of choose
    – Ferrybig
    Commented Mar 24, 2022 at 11:53
14

Press CtrlC, then Up, then re-edit your command, then press Enter.

# git commit -m "Nettoyage :
>      - Suppression de sources ou projets inutiles
>      - Corrections mineures sur les sources pour supprimer des warnings
>      - Autre chose^C

By pressing CtrlC at the end of the line, I lose that line, but with one single Up keystroke, I get back:

# git commit -m "Nettoyage :
     - Suppression de sources ou projets inutiles
     - Corrections mineures sur les sources pour supprimer des warnings

with my cursor placed after the 'ings'. I can then navigate back and forth through all three lines of text using Left and Right, to the beginning of the command with Home or CtrlA, to the end of the command with End or CtrlE, etc.

I'm not using any exotic readline settings, to my knowledge. Seems to be standard behavior of Bash 5.1.16 on FreeBSD 13.0.

6
  • 2
    This doesn't work with a multi-line command. At least not on my system. Hitting Ctrl+C and then Up only lets you edit the first line and has lost the rest. Does your system behave differently?
    – terdon
    Commented Mar 22, 2022 at 6:42
  • 4
    @terdon MacOS, bash 4.2.46, ^c followed by up arrow/^p causes all the lines to be drawn and I can cursor across them.
    – BowlOfRed
    Commented Mar 22, 2022 at 7:08
  • 2
    Weird. Maybe a readline setting or something, @BowlOfRed? On my Arch Linux with bash 5.1.16, Ctrl+C and then up only brings back the parts of the command until the last newline entered.
    – terdon
    Commented Mar 22, 2022 at 7:46
  • I get the same behaviour as terdon with Bash 4.4.20 on RHEL 8.5, but the same behaviour as Jim and BowlOfRed with Bash 5.1.4 on Debian 11 and Bash 5.1.8 on Fedora 35. Commented Mar 22, 2022 at 8:53
  • 1
    Works for me with 5.1.16(1)-release (x86_64-pc-msys), mintty. Commented Mar 22, 2022 at 18:18
7

You don't. Or not as far as I know, I hope someone else will post an answer and prove me wrong! The best thing I've found so far is to hit Enter to move to a new line (without this, on my system, you don't get back the full command in the later steps but only the part before the last newline entered) and then Ctrl + D to abort the command:

$ git commit -m "Nettoyage :
>      - Suppression de sources ou projets inutiles
>      - Corrections mineures sur les sources pour supprimer des warnings

Now press Enter:

$ git commit -m "Nettoyage :
>      - Suppression de sources ou projets inutiles
>      - Corrections mineures sur les sources pour supprimer des warnings
>

And now Ctrl + D :

$ git commit -m "Nettoyage :
>      - Suppression de sources ou projets inutiles
>      - Corrections mineures sur les sources pour supprimer des warnings
> bash: unexpected EOF while looking for matching `"'

You can now press (up) to get back to the previous command and now you will be able to edit it as expected:

$ git commit -m "Nettoyage :
     - Suppression de sources ou projets inutiles
     - Corrections mineures sur les sources pour supprimer des warnings

Not perfect, but it might work as a workaround.

2
  • 4
    With respect, @terdon, better IMO to hit Ctrl-C to abort, than to press Enter. Enter could result in the execution of a syntactically correct command which is contextually incorrect.
    – Jim L.
    Commented Mar 22, 2022 at 0:36
  • 1
    @JimL. thing is, Ctrl+C doesn't work: it only lets me edit the first line of the command, as I mentioned under your answer. As long as you hit enter before typing the closing ", it should be perfectly safe and you won't execute anything, and Ctrl+D at least lets you get access to the whole, multi-line command you were editing.
    – terdon
    Commented Mar 22, 2022 at 6:43
7

For your question: How to write multi-line shell commands that let you go back to the first line

Instead of hitting Enter at the end of the first line, use Ctrl+V Ctrl+J. That'll insert a newline without entering the command to bash. No new prompt (like >) will be printed, because to bash, it's still the same entry. If you had done that, you would have been able to go back to the first line with Ctrl+A when using the default emacs keybindings or ^ when using the vi keybindings.

Here's an example with bash using the default emacs bindings:

multi-line prompt entry example with bash emacs bindings

If you use zsh instead of bash, then you can use the Up/Down keys, or Ctrl+P/Ctrl+N with default emacs keybindings, or j/k with vi keybindings to move between the lines of the single prompt entry.

If you use zsh with vi keybindings, you can also use o/O in normal mode to create new lines below or above the current one in the same prompt entry. Those are the keybindings that I normally use in zsh:

multi-line prompt entry example with zsh vi bindings

For your situation: How to fix the start of your git message from the end of the command

Just add -e at the end of the command, and git will take you to the editor with the contents of the -m argument(s).

Here's an example of what you could've done in your particular situation:

git edit example

6
  • This does indeed work, with one slight difference in my tests — I get the > prompt after C-j. Commented Mar 23, 2022 at 5:16
  • @StephenKitt Thanks for the heads-up. I do this so often I hadn't noticed I prefix the binding with Ctrl-v. I've updated the answer.
    – JoL
    Commented Mar 23, 2022 at 5:27
  • That’s interesting — I’d tried it without C-v, and without that, C-j produces a new line which is logically separate (C-a takes you to the start of the line, not the start of the complete command), but arrow keys work and allow you to move up and down the command. The resulting history is rather strange though (I only get the first line, up to the first C-j), and the display can get messed up while editing. Commented Mar 23, 2022 at 8:19
  • @StephenKitt If you mean that you're getting PS2 printed, but you can use the arrow keys to move back to the PS1 prompt, it makes me think that maybe you don't have readline running or bash isn't reading raw or something? It's sounds like how the terminal cursor behaves on an old SCO system. You can move the cursor anywhere, delete the prompt, etc. but it just feeds the corresponding escape sequences for the arrow keys, etc. to the shell as part of the command arguments.
    – JoL
    Commented Mar 23, 2022 at 10:03
  • Yes, PS2 isn’t empty, and Bash isn’t joining the commands. The cursor stays on the same line, it’s the content that gets replaced as I move up and down. But I was jumping to conclusions, moving up goes up in the history, not the lines in the command. See asciinema.org/a/ZwycT1sxvg68PYrkj6YSMG7vn Commented Mar 23, 2022 at 10:08
1

As an alternative to typing a multi-line string literally, you can use Bash's backslash-escape-expanded string, like

git commit -m $'Nettoyage :\n     - Suppression de sources ou projets inutiles\n     - Corrections mineures sur les sources pour supprimer des warnings'

This lets you flatten the multiline string into a single line and use \n to denote line feeds. The resulting command will have real line feeds in the string.

2
  • Yes, it's possible. But on everyday life I don't see myself typing this command that is complex to review (to check if it will produce the wished formatting). Commented Mar 24, 2022 at 14:50
  • @Marc to preview you can prepend echo and press Return. Then, after you make sure it's OK, recall the command, remove the first word (via Ctrl-A, Alt-D) and press Return. Not as simple as in JoL's answer, but still.
    – Ruslan
    Commented Mar 24, 2022 at 16:52

You must log in to answer this question.

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