5

Often I'm writing a command in a bash prompt, where I want to get previous arguments, in the CURRENT line I'm typing out, and put in other places in the command.

A simple example, would be if I want to rename a file. I would

  • type out the mv command
  • type the filename I want to move, ~/myTestFileWithLongFilename.txt
  • now I want to just change the extension of the file that I supplied in the first argument, without typing it again.

Can I use history or bash completion in some way to autocomplete that first argument?

$ mv ~/myTestFileWithLongFilename.txt ~/myTestFileWithLongFilename.md

I know of course I could execute the incomplete command, to get it into the history, and then reference it with !$, but then my history is polluted with invalid commands, and I'm wondering if there's a better way

Brad Parks
  • 1,669

4 Answers4

10

To lessen the amount of typing needed for that mv command, a brace expansion could be used:

mv ~/myTestFileWithLongFilename.{txt,md}

The brace expansion expands to ~/myTestFileWithLongFilename.txt followed by ~/myTestFileWithLongFilename.md (it's important to get the {txt,md} order right).

Brace expansion work in bash regardless of whether you type them on the command line or in a script.

A brace expansion, prefix{word1,word2,word3}suffix, would repeat the prefix and suffix string for each wordN in the actual brace, generating the three separate words prefixword1suffix, prefixword2suffix, and prefixword3suffix. You may also use ranges, such as {1..4} or {p..u} in brace expansions.

Kusalananda
  • 333,661
  • Thanks - didnt quite get that's how that could work... nice - but what if the argument I wanted to have it show up in was the 5th argument, for example? (ie not a mv command, but something else) – Brad Parks Mar 24 '21 at 18:54
  • @BradParks I only covered the command that you actually showed. If you can show a command in which you have to repeat the fifth argument with some modification, then maybe I could give you some example of that too. The nice thing about the brace expansion that I showed is that it works in scripts as well (since it doesn't rely on history expansions). – Kusalananda Mar 24 '21 at 19:01
  • agreed - it's definitely a short and concise way of doing what I do about 75% of the time, so that's great! – Brad Parks Mar 24 '21 at 19:04
10

It's possible but a bit cumbersome. In bash !# refers to the entire line already typed. You can specify a given word you want to refer to after :, in this case it would be !#:1. You can expand it in place using shell-expand-line built-in readline keybinding Control-Alt-e.

2

Interactively, alt+1 then alt+. (period).

Repeat alt+. to go farther back in your command history, extracting token #1 from commands before the most recent. (The command itself is token #0, alt+0)

Without the alt-<token-number> modifier, alt-. takes the last arg of the previous command. e.g. mkdir foo / cd alt+. return to make a directory and CD into it.

Normally for anything other than the last token (one modified keystroke) I'd just up-arrow, control-w or alt+d then control-y to kill the word and yank it back to not mess up my history, then down-arrow and control-y to yank it again where I want it in the command line I'm editing. This is in bash, BTW.

Peter Cordes
  • 6,466
  • This is very cool :-) unfortunately for me, my GUI terminal emulator steals Alt+1. I suspect some other people will be in the same situation. – David Z Mar 25 '21 at 04:59
  • @DavidZ: So does mine; I used esc-1 to test this. But that makes it even slower to use, even when you do remember how the indexing works and can mentally count faster than you can up-arrow and move the cursor around with ctrl-left or ctrl-right to go by words. Like I said, I generally only use unmodified alt-. which Konsole passes through just fine, and up-arrow / kill/yank to copy/paste for other things that aren't at the end of a line. – Peter Cordes Mar 25 '21 at 05:06
1

With set -o vi, press escape, byw$p (you may need to add spaces or such, and you may need B or W). Then you just have to touch things up.