Bash history expansion with tab completion
Asked Answered
D

4

6

I make frequent use of command history expansion using the form !n:m, where n is the number of the historical command and m is the number of the argument, or short forms thereof.

Is there a way to expand such arguments in situ, so that I can then tab-complete them?

A trivial example:

$ mycmd long/path/with/plenty/of/subdirectories/
$ cp !$/tediously-verbose-filename.txt .

I'd love to be able to use argument repetition without having to then type the filename out in full (or resort to dummy runs with ls or echo).

Dollhouse answered 2/4, 2014 at 21:13 Comment(0)
E
3

The conceptual terms you're looking for is [command] history and history expansion - if you run man bash, you'll find sections HISTORY and HISTORY EXPANSION devoted to this topic.

That said, in this particular case, it is not history expansion, but the special shell variable $_ that is your friend:

It expands to the last (expanded) argument of the most recent command.

Try the following, which mimics your scenario:

ls "$HOME"  

# Type this on the command line and press TAB (possibly twice)
# _before_ submitting to TAB-complete to matching files in "$HOME"
# (irrespective of what the current directory is).
# $_ at this point contains whatever "$HOME" expanded to, e.g. "/Users/jdoe".
cp $_/  

Note: Whether tab-completion works for a given command is unrelated to whether $_ is used or not. See man bash, section Programmable Completion, for how to manually enable tab-completion for commands of interest.

Elocution answered 2/4, 2014 at 21:50 Comment(7)
Thank you for the terminology. Should I update my question to reflect this?Dollhouse
@KateMcKenzie: Please do.Elocution
$_ sounds like what I want. And it does indeed contain the last argument of the most recent command. Sadly, it's not working for me, but perhaps I'm misinterpreting "Type this and press TAB before submitting ...".Dollhouse
@KateMcKenzie: What bash version, what platform (mine: bash 3.2.51 on OS X 10.9.2)? What, specifically, isn't working? What I meant is: on the command line, type 'cp $_/<start-of-filename-of-interest>' without pressing ENTER, then -press TAB (potentially twice, depending on your settings) to expand to matching files in your home folder.Elocution
Ahhh, I didn't quote the argument to the first command. Thank you very much for your help!Dollhouse
Actually, that was premature. It seems to work for echo but not for anything else. The dozen or so $__git variables may not be helping. I'll look further into the way bash is expanding env vars. I'm using GNU bash 4.2.25 on Linux Mint Maya.Dollhouse
@KateMcKenzie: Please see the end of my updated answer; bash enables only a few completions out of the box (and cannot anticipate all the ones that make sense).Elocution
G
3

Not exactly what the OP wants, but you could use readline shortcuts to retrieve a specific argument from a prior command, and the shortcut is Alt+.. For the specific example that OP gave, this works very well, with the following workflow:

$ mycmd long/path/with/plenty/of/subdirectories/
$ cp <Alt>+./tediously-verbose-filename.txt .

Here are a couple of additional things that I find very useful:

  • If you continue to press Alt+., it retrieves the last argument from prior commands in the history.
  • You can prefix Alt+. with Alt+<n> where n is a digit from 0-9 to retrieve the specific argument. E.g., Alt+0Alt+. would retrieve the command itself and Alt+1Alt+. would retrieve the first parameter etc. You can then continue to press Alt+. without having to repeat the prefix.

NOTE: On terminals where Alt combination doesn't work, you could use Esc prefix, e.g., <Esc>..

Galwegian answered 9/2, 2015 at 5:54 Comment(0)
G
2

Yes, there is, use the following:

cp !$:h/tediously-verbose-filename.txt

The :h part will bring the directory portion of the command argument

Gamaliel answered 2/4, 2014 at 21:15 Comment(2)
That isn't actually what I was after, although I can see it would be useful in other situations. Perhaps I haven't been sufficiently clear in my question; I'll try to improve it.Dollhouse
This doesn't work directly as @KateMcKenzie intended, but it does so in a 2-step process: first, type and submit cp !$:h/ (note the terminating /) - this will paste the command with the previous command's last argument substituted for !$:h onto the command line, followed by /, allowing you to then TAB-complete; for a 1-step solution, see my answer.Elocution
O
0

I know this is old but looks like only option im seeing is running the command with :p to preview by printing to the screen, then arrow up in history to run it after editing.

I'm curious if a function could be created that pulls the results of !!:p then returns it to the line before the command is actually ran, initiated by a tab after !! for example. Ill post it here if i manage to figure out how to write it.

but here is the example of what i mean:

cd@PC:~> _ zypper install git
Loading repository data...
Reading installed packages...
'git' not found in package names. Trying capabilities.
No provider of 'git' found.
Resolving package dependencies...

Nothing to do.
cd@PC:~> !!:p
_ zypper install git
cd@PC:~> _ zypper install git-core
Obituary answered 6/1, 2018 at 19:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.