Repeat Last Argument at Command Line

Here’s a handy tip when you want to rename a file with a long path/name and don’t want to retype it twice.

I use Hugo for this blog and each new post starts with this command: hugo new post/post-name which then generates a file at content/post/yyyy-mm/post-name.

But I like using Markdown, so that command should really be hugo new post/post-name.md. When I inevitably forget and recognize this after running the command, I then need to rename this file to include the .md extension.

# broken onto two lines just to make it more readable
mv content/post/2020-09/some-long-article-name-with-lots-of-seo-goodness \
   content/post/2020-09/some-long-article-name-with-lots-of-seo-goodness.md

It’s a little tedious to retype that long path/file. Here are two suggested approaches:

The first technique is called brace expansion. It lets you generate multiple strings by putting a comma separated list of values within braces.

In my example, if I want to rename the file to include the .md extension, I can do this:

mv content/post/2020-09/some-long-article-name-with-lots-of-seo-goodness{,.md}

You can have multiple brace patterns within your string, and you can do more than just two values. I recommend checking the docs for more examples.

The second technique lets you auto-complete a value in the middle of your command.

After I’ve typed mv content/post/2020-09/some-long-article-name-with-lots-of-seo-goodness, instead of re-typing the whole file path as a second argument, I can type !#$ and then press Tab and it auto-completes the first argument a second time. Now I can append .md and hit Enter.

What is going on here? We’re combining two different concepts. !# is called an event designator and it let’s you refer to your command history. You could use this to refer to a command you typed 5 commands ago, but !# refers to the currently command being typed right now.

Next, the $ is called a word designator. Just like events, you can refer to a variety of different word histories. $ means “the last word”.

Putting those two things together, !#$ means “the last word of the current command”, which in our case is that long file path we don’t want to retype. Pressing Tab then performs the substitution.

There are a lot more uses for this type of behavior. Again, I recommend checking the docs. The history expansion topic covers this and more.

Note: even though I’m linking to the Bash docs, this works in zsh as well, which is my shell of choice.