Fish-shell: support history expansion (e.g., bash's !!, bang bang, and !$, bang dollar, tokens)

Created on 20 Aug 2012  ·  98Comments  ·  Source: fish-shell/fish-shell

It appears that the double-bang does not work and is somehow interpreted as a program which sudo unsuccessfully tries to locate. This occurs on Ubuntu 12.04 LTS with fish-shell compiled from source.

Steps to repro:

  1. Execute any command:

    $ ls

  2. Execute the previous command as root:

    $ sudo !!
    sudo: !!: command not found

    Expected behavior:

Command sudo !! executes previously executed command (ls) as root.

I also checked that the issue isn't caused by sudo by following repro steps in bash, which behaved as expected.

Also, note that an earlier version of fish-shell on Mac OS X 10.7 did not have this problem and sudo !! worked as expected.

duplicate

Most helpful comment

function sudo
    if test "$argv" = !!
        eval command sudo $history[1]
    else
        command sudo $argv
    end
end

sudo !! for people who still use !! for some reason.

All 98 comments

It is discussed in documentation FAQs : http://fishshell.com/docs/current/faq.html#faq-history

Thanks for the clarification.

I understand the rationale for removing history characters. However, while I understand the rationale for this and that it is possible to use C-e and C-a instead of End and Home, for many users, especially those used to !! and those on a laptop that do not have Home and End keys, it is still much easier to type sudo !! than Up+C-a+sudo.

Would you still consider restoring functionality of history substitution tokens?

Since it's a rare case where !! is more comfortable to use, try the following function:

function sudo!!
    eval sudo $history[1]
end

which is called just by "sudo!!".

Or the other possibility which I use:

function .runsudo --description 'Run current command line as root'
    commandline -C 0
    commandline -i 'sudo '
    commandline -f execute
end
bind \es .runsudo

Then by pressing M-S I run the current command line with sudo put just in front of it.
Or by modifying this example you can make a function which will put the previous commandline in the current one and bind it for example to '\e!'. This approach is better then simple use of '!!' because it gives you the opportunity to see and update the commandline before the execution.

I made a slight variation of the last command:

  function .runsudo --description 'Run current command line as root'
    set cursor_pos (echo (commandline -C) + 5 | bc)
    commandline -C 0
    commandline -i 'sudo '
    commandline -C "$cursor_pos"
  end
  bind \es ".runsudo"

It saves the cursor position instead of outright executing it.

!$ would also be nice to have

function sudo
    if test "$argv" = !!
        eval command sudo $history[1]
    else
        command sudo $argv
    end
end

sudo !! for people who still use !! for some reason.

Thanks. There is a very sizable chunk of people who use sudo !! give that this syntax is supported across many other shells including bash and zsh. sudo !! is easier to type than using the home and end keys since those keys are farther away.

I would suggest having the above function be included with fish-shell by default.

!! is a good candidate for an abbreviation (#731)

Expressing support for !! and related shortcuts. It doesn't hurt to add it and it feels like fish is just shoving personal philosophy at its users.

My current thinking of the ideal solution is that !! ought to be an abbreviation (#731), and we implement function signatures (#478) to support subcommands (a command that takes a command, like sudo). Then abbreviations expand in subcommand positions, so that 'sudo !!' will expand.

Adding myself

Despite it being old, it's a feature that really should come as default in Fish. There's no reason not to have it - if you don'r want to use it personally, that's fine, but having an extra command doesn't really hurt anyone - people like doing things different ways.

The way !! is implemented in most shells does hurt people. For example, consider this command:

echo fish is great!!

in bash / zsh / tcsh, this will do something unexpected and horrible, because the !! is magic syntax. This is the sort of weird interactions that fish tries very hard to avoid.

My suggestion was to implement !! not as magic syntax, but as an alias. This means it would only do something special in 'command position' - it won't expand in arguments. It also keeps the feature set of fish down, which is desirable.

To make that useful, we would need to teach fish about subcommands, with sudo being the most common example, but also time, strace, etc. fish would then expand aliases in "subcommand position", which is a cool and useful feature in its own right.

Your proposed solution falls apart when you consider sudo echo fish is great!!. The same argument can be made of all syntax: isn't echo I really like fish (the shell)! also confusing? I also worry that a list of "subcommands" would be impossible to maintain and would reduce flexibility.

With aliases, only commands get expanded, and fish would know which arguments are (sub)commands and which are not. So sudo echo fish is great!! would not perform last-command-expansion, but instead pass a literal "great!!" to sudo (and thereby to echo). I claim that is what the user expected. See the discussion about abbreviations (#731) and function signatures (#478) for the design and rationale.

SirCmpwn is correct that requiring comprehensive function signatures is a disadvantage of the alias approach, and also that other syntactic elements are confusing. Real-life examples include the annoyance of brace expansion with git (#434) or wildcards (#967, among others). Syntax comes with a high cost, and we try to eliminate syntax when it's redundant with other features (e.g. #354).

Anyways that's my argument for why supporting !! as an elementary syntactic element is a no-go. However I'm open to introducing it in ways that don't require modifying fish syntax.

I think the comparison with {} is a poor one. In fish, braces work differently than other shells. People only trip up on it when they expect it to work like bash and friends. The !! syntax should work the same as other common shells. Also, !! isn't something I'd expect to see in commands often enough to cause issue. In fact, I'd expect the number of times people have tried and failed to use !! with fish far outnumbers the number of times someone has used a literal !! in a command.

I claim that ! is confusing in other shells, and it would be better if it wasn't implemented this way. For example, at http://codegolf.stackexchange.com/a/17776/3103 I put simple "Hello, world!" program written in UNIX shell that fails because of ! (in Ridiculous Runtime Errors question). It even got seven upvotes, so I can imagine this feature can be surprising when you don't want to use it.

However, !! by itself should be fine, because how often user will write !! as a token? Then again, adding !! would encourage adding other history substitution patterns, some of which could be annoying.

Adding myself - just ran into this my first weekend with fish. Should definitely be supported.

Adding myself - need this.

For those desiring this, the function and bindings mentioned earlier in the discussion have given me an easier workflow for prepending sudo or sudoing the last command than the typical sudo !! one.

You should give them a try. It made me go revisit my readline and zsh configs to have similar bindings.

It turns seven keystrokes (two of those shifted) into a single binding.

Regardless of what _you_ like - is there really any reason for !! not to be supported? Everyone has different things that work for them, and though some people might enjoy using the up arrow, a lot of people enjoy using !!, and honestly, why would it be a bad thing? Those who don't want it don't have to use it.

Surely it's not that hard to implement?...

Yes, I know you can hack it together with fish scripting, but it does _not_ work in the same way as in bash or similar shells, which was quite convenient a lot of the time.

See my comment above for my rationale for why I don't want to introduce this the way that bash and zsh do it (as a new syntactic element), and my proposal for a way it could be done that keeps with the fish philosophy.

The argument of "echo fish is great!!" being better than using '!!' for history is a bit weak, but I understand the point. Seems like a simple "set shell_history advanced" or something could allow it for people willing to risk "echo fish is great!!" getting an error message.

Nevertheless, my use case of '!!' is similar but could be avoided with some feature enhancement perhaps.

For example on my mac with bash:
$ brew update
$ brew outdated
$ brew fetch !!

So I somewhat regularly use the output of the previous command for a new command.
With fish I can do this:
$ brew fetch (brew outdated)
Now at some point perhaps that will be a suggestion I can use, but what I really need is to be able to search the history within the () block.
e.g.:
$ brew fetch (bre)

Unfortunately even though fish is fairly proud of using () for sub-commands instead of "confusing" backticks and such, it doesn't really treat them as such until you hit enter. At least that is what I see.

If fish would allow a history search after the first '(' (aka a subcommand) I think it would be easier to handle not having ! history manipulation.

If I understand the suggestion, it's that when you press up arrow, it should perform history replacement only within the innermost subcommand instead of the entire command line.

:-1:

I still support simply implementing bash-style !!.

I'd prefer :up_arrow:, Ctrl+p and command history search working in subcommand positions over introducing !!

The entire history API of other shells is confusing enough that people usually only remember !! from it.

Correct. That would be convenient and fairly easily replace the '!!' Or !keyword use.

Really like the idea of !! being an abbreviation.

It looks like abbreviations in the most recent nightly builds are only allowing abbreviations in the command position, hopefully it'll be updated to be able to be used as arguments for cases like this one.

This feature is overrated, just use sudo cmd or eval sudo $cmd.

@bucaran I am not following exactly. The !! is for speed, how do I match the speed of !! with cmd?

Do you mean make a variable called $cmd that is the last command? It isn't a native variable.

Thanks

@mdsib Yeah, I think I remember that there was a discussion in another issue about not allowing abbreviations in the argument position. At the time it was preferred to not implement that. This would definitely change that.

I think the takeaway from this thread is that it's not happening any time soon, and I should unsubscribe from this Github issue and switch to zsh. Adios!

Not because !! isn't here, but because a Github issue for a fairly simple feature has been open for 3 years without being implemented due to philosophical BS.

It is funny to see people trying to convince others that a feature isn't a big deal. Sure, you don't want it, so you don't care.. It is like you're trying convince people not to like green because you think blue is better.
Nevertheless since a "simple" feature can't seem to make it in, perhaps someone should just fork fish if they like the existing features but would like to add a few more. Seems like that would get somewhere quicker.

I made ~/bin/!! that looks like this:

#!/bin/fish
eval $history[1]

For the short term it helps me workaround the missing feature and my tendency to type !!.
I also made ~/bin/!vi:

#!/bin/fish
for f in $history
    echo $f | grep '^vi'
    if test "$status" = "0"
        eval $f
        exit $status
    end
end

Not ideal, but enough that my shell tends to do what I expect more often now.

@SirCmpwn Is it not obvious to me that !! is a simple feature. For example, bash supports this:

ls some_long_file.txt
!!:s/ls -l/cat/

Should this use of !! be supported or not? Users who say "fish should support !!" may have different ideas of what that means.

You feel strongly about this, so I encourage you (or anyone) to author a proposal for how !! would work in fish (the semantics, not implementation). What syntax would be supported exactly? How would this expansion relate to other types of expansion, like variable or process expansion? How would argument splitting occur in the expanded string? Could you use escapes to prevent it?

This is a sincere request. A feature is not defined by a single use case; we need a concrete proposal to discuss.

If you say "I want this case to work, and I don't care how," then that's fine and valid. But someone has to work out the details before anything can be implemented.

In the near term, one concrete plan is to implement global aliases and then make !! into one. An alternative is to merge the docopt work, give a function signature for sudo, and then make !! into a regular alias. Which direction we go hinges on whether we think !! should expand everywhere, or just in command position.

@ridiculousfish You're absolutely right that a '!!' feature request needs to be fairly detailed. I've never used the particular example you gave as I always do something like this:
$ ls -l some_long_file.txt
$ ^ls-l^cat

Obviously this doesn't work either. That would be separate feature request for me, that I haven't bothered with since I only started testing fish out recently.

"fish: Encountered redirection when expecting a command name. Fish does not allow a redirection operation before a command."

Seems like it would be easy to treat '^' in the command position as a substitution since it isn't conflicting with existing functionality. But that is a separate issue, just mentioning it since you're right that there should be a clear definition of what '!!' (or just history substitution in general) means.

Here are a couple examples I use:

$ !!
$ some command `!!`
$ !keyword 
$ !123

Thank you @gillham , information like that is very helpful when designing a feature.

I also note that @nyarly has a clever workaround for !! and !$ here: https://github.com/fish-shell/fish-shell/wiki/Bash-Refugees

Better fix #5:
Fish is so history driven, that when having forgot sudo or otherwise polluted your history with a non-working command, it may actually be worth your time (in the long run) to manually erase it from history. Yes, that's open a new terminal, exec bash and edit ~/.config/fish/fish_history. Fish has led me onto the wrong command time and time again, when I have skimped on this.

As for @nyarly's mkdir …; cd !$ habit, I'm doing mkcd …:

function mkcd --description 'mkdir and cd'
        mkdir $argv[1]
        and cd $argv[1]
end

Note that erasing from history doesn't require bash shenanigans:

history --delete --prefix some_command

fish_config history also lets you do it by point-n-click.

This functionality can be replicated quite well within Fish using https://github.com/fish-shell/fish-shell/wiki/Bash-Style-History-Substitution-(!!-and-!$) -- perhaps the issue should be closed?

:-1: :-1: Unless a full bash compatibility mode is implemented.

I think this is a slippery slope. If this is implemented in fish, what about single !s, and what about ksh history features? What about $()? Fish is a csh-style shell. If people can't/won't adapt, there's zsh...

What I have gleamed from this discussion as a tl;dr for people who find this. (over 3 years in)

  1. The team does not want to develop the feature, because they don't use it.
  2. There are a lot of people who want the feature, because they use it.
  3. The team seems unwilling to implement the feature as it exists but is willing to make something
    that works similarly if we give them enough data.
  4. People either accept not having the feature, implement a workaround that mostly works, or move
    on to another project.

I am one of the latter, while I hope this get's resolved I cannot reasonably use fish and add that workaround with the number of machines I use without a considerable amount of effort (I need different config setups in different machines), and I use !! enough where it is a requirement for me. So I will be following this discussion in the hopes that one day fish will meet my use case.

@tetra-archos Nobody has put forth a concrete proposal yet, so we don't have a firm grasp of what "the feature" is. Since you obviously feel strongly about this, if you were so inclined, it would be a big help if you were to suggest what the behavior ought to be, as a starting point. Failing that, it would be useful to at least list the history substitution forms you use.

Note that the full bash history substitution behavior is very complex, and also configurable, so "just do what bash does" is a non-starter.

Nobody has put forth a concrete proposal yet, so we don't have a firm grasp of what "the feature" is.

@tetra-archos: This is absolutely critical. So far, most of this has been 'We want "!!"!!' and maybe "!$" (which is "insert last argument of the last command"). But there's a lot of details that need to be banged out if it (or a substitute) is to be implemented.

For example:

  • Which part of the history substitutions do you want - is it just "insert the last command here" ("!!") and "insert the last command's last argument here" ("!$"), or all the other stuff like "!!:s/string1/string2" (I'd rather add a general "replace" mode like emacs' "query-replace" or vim's ":s/" so you can also do it _before_ executing the wrong command)? How about "insert the fifth command in my history" ("!5") - which I never quite got - or "insert the fifth-last command" ("!-5")? How about "word designators" and "modifiers"?
  • Do you want the command to execute right away (i.e. you enter sudo !!, press enter and the entire thing executes right away) or would it be okay to have it inserted first so you can double-check - this is like bash with the "histverify" option (I'd lean towards the latter)?
  • Does it need to be "!!" and "!$" or can it be used differently , i.e. is muscle-memory your problem? If that's the case, though, we're never gonna be 100%, and history substitution _is_ a rather small part of bash

If it's just muscle-memory, you can also put the following in your fish_user_key_bindings:

bind '!!' 'commandline -i "$history[1]"'
bind '!$' 'history-token-search-backward'

(the issue this has, why we probably shouldn't take it in the default bindings, is that it then waits after you've entered "!" to check for a second character)

I think that !! is bad practice

The command sudo !! does not tell anyone what the shell will actually execute.
if i want execute the previous command with sudo at the 'anfang' i type

^p ^a sudo

I suggest adding a message to tell people to use the line editing commands ^p and ^a instead of using the history function.

!!: command not found, try 'Ctrl-p Ctrl-a' to prefix the previous command with 'sudo' or any other  prefix

I hate !! too. An easier method for fish_vi_mode as an alternative to ^p ^a:

^v k I

Because you don't like a way of doing something doesn't make it less valid.
On Nov 10, 2015 8:14 AM, "Ivan Tham" [email protected] wrote:

I hate !! too. An easier method for fish_vi_mode as an alternative to ^p
^a:

^v k I


Reply to this email directly or view it on GitHub
https://github.com/fish-shell/fish-shell/issues/288#issuecomment-155416048
.

The reverse is true, too.

Both sides of this issue have legitimate needs and concerns. I think the main holdup is finding the _right_ way to do it instead of the quick and dirty way

I agree and I have nothing against the other ways to do it, they just don't
fit my (and obviously other people's) needs.
On Nov 10, 2015 12:19 PM, "Eric Mrak" [email protected] wrote:

The reverse is true, too.

Both sides of this issue have legitimate needs and concerns. I think the
main holdup is finding the _right_ way to do it instead of the quick and
dirty way


Reply to this email directly or view it on GitHub
https://github.com/fish-shell/fish-shell/issues/288#issuecomment-155494929
.

To reiterate the approaches mentioned in the early discussion and to expand on them a little: I have found keybindings to work better for me than "!!".

I've written this function which can be bound to some key (^S for me, bind \cs 'prepend_command sudo'). If the command line has content, it prepends sudo. If there is no content, it prepends sudo to the last item in the history. So sudo !!<Enter> now becomes ^S<Enter>.

function prepend_command
  set -l prepend $argv[1]
  if test -z "$prepend"
    echo "prepend_command needs one argument."
    return 1
  end

  set -l cmd (commandline)
  if test -z "$cmd"
    commandline -r $history[1]
  end

  set -l old_cursor (commandline -C)
  commandline -C 0
  commandline -i "$prepend "
  commandline -C (math $old_cursor + (echo $prepend | wc -c))
end

Some people said configuring something like this is too complicated to do. A possible solution might be to include something functionally similar to my function in fish so configuring this becomes a one-liner at least.

Of course, this doesn't answer other demands like some_command --with-subshell-param (!!).

@Ahti That is actually badass, just got it working, looks like it will be a treat to use, will report back in a while!

Yeah, so far this beats sudo !!. Being halfway through a command and realizing it should be sudo happens to me a lot. ^s, instant sudo prefix!

Personally I never used the !! in any context other than sudo !! so that doesn't bother me.

Coincidentally I just tried out fish_vi_mode and really like it. However, in Vi mode ^s doesn't appear to bind anymore. Suggestions?

Okay, I had the key binding in config.fish. I found #2254 and then found https://github.com/faho/fish-shell/commit/f7e8ad632a388d5f1c5eb4cfc33f3cdb1f7fd831 and then found http://stackoverflow.com/a/16675092/292408 which confirmed I should place my bindings in a fish_user_key_bindings function which didn't yet exist.

That worked and now when I am in Vi command mode [N] the prefix works as expected.

We already have Alt-P to run the current pipeline in the pager; I wonder whether we should add Alt-S as well?

I think @Ahti's is a pretty elegant solution to what everyone has been looking for. Direct to a keybind with no worry about a random expansion or weird edge cases. It simply places text for you to use or cancel out as you wish.

I like both @nyarly's substitution and @Ahti's key binding solution.
Personally, I use !! only in the context of sudo !!

Oy! This thread is almost four years old now. I recently bought ǃǃ.com and ǃǃ.net (which Github won't let me hyperlink, apparently, so copy-and-paste) if anyone wants them.

Hey! That's cool! @geoff-codes How did you do that?)

They've recently started allowing .com and .net "internationalized" domain names.
screen shot 2016-05-11 at 4 53 48 am

Support for it is still pretty buggy at this time, though. For example, they let me register ⵌⵑ.com but not ⵌǃ.com.

I didn't know you could register !!.com, that's crazy.

The lesson learned from this issue: if you ever write a Unix shell, the only feature you should make sure to implement is sudo !!, because it seems people can't live without it.

Seriously though, I've got word from people saying that this is working great:

@bucaran Yes, indeed, that seems to be the case 😆.

Internationalized Domain Names - IDNs:

https://en.wikipedia.org/wiki/Internationalized_domain_name

edit: too slow with this post, and also registrations like those @geoff-codes! That's got to be a wise investment. A two character (sorta) .com!

#!.com would have taken the cake though. Is there a rule prohibiting that for the com TLD, or something they should resolve?

In the bash for Windows presentation the guy presenting typed something
like this:

$> echo hello Windows!!

And I hope that that you can all guess what happened.
On 11-May-2016 1:57 PM, "Aaron Gyes" [email protected] wrote:

International Domain Names - IDNs

On May 11, 2016 at 4:44:58 AM, Timophey ([email protected]) wrote:

Hey! That's cool! @geoff-codes https://github.com/geoff-codes How did
you
do that?)


You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub
<
https://github.com/fish-shell/fish-shell/issues/288#issuecomment-218435761


You are receiving this because you commented.
Reply to this email directly or view it on GitHub
https://github.com/fish-shell/fish-shell/issues/288#issuecomment-218438118

So, I should switch to bash?

@base698 - I wound up putting out a fisherman plugin to support the two most common use cases of history substitution: !! for the whole last command and !$ for the last argument. Honestly, I've mostly broken my own habits because fish's command line editing are so good. Still, I really like being able to

> which tool
> cat (!!)

to examine a script for instance

You can install the plugin with fisher install nyarly/fish-bang-bang once you've got fisherman installed.

for the sake of completeness (and giving a correct example)
here is a screenshot of the youtube video I mentioned, this is one of the great features of the '!!' pattern which is now avaible on Windows too.
bangpatternbash

https://www.youtube.com/watch?v=2dB0igTfhfg&feature=youtu.be&t=760

He wants to execute git commit -a -m "Rawr!!"

results in git commit -a -m "Rawrclear"

As much as I love fish, !! is just too useful. Not sure sudo !! but also as a way to pass previous info to new commands. For example:

find . -name something | grep "pattern" then I would play with the pattern until I get what I want.
Then I would pass it to something like so (someCommand !!). Currently, in fish, this requires too many steps. The workarounds don't work well enough for me.

zsh expands !! so that you can edit it. So gonna have to go back to zsh.

If you're using fisherman (which you very much should be) you can install nyarly/fish-bang-bang and get anywhere-in-the-command expansion of !! and !$

@nyarly Yeah, I tried that. But I don't like how it expands immediately after the second ! is typed. It felt awkward. zsh feels better because it expands after you enter a space or hit enter.

EDIT: Nevermind, I couldn't do it. I had forgotten how annoying setting ZSH to be what I want it to be was. nyarly's plugin will have to do.

I'm so glad this was not implemented! It violates Fish's Law of orthogonality and Law of user focus. Bash's history replacement requires too much thinking beyond !! and !$ and maybe s^. That's why people only use those. Fish's solution is more interactive, which allows more general replacement of arguments. It is efficient enough in terms of number of strokes, and equally efficient with !!.

Some users miss !! and !$ and don't want to learn a different way. Should Fish implement just these features as edge cases (see Law of orthogonality)? Or all of Bash history replacement? Or all features from other shells? All features that users can think of?

Here's a fix for anyone who wants something to copy and paste:

put this into ~/.config/fish/functions/fish_user_key_bindings.fish

function bind_bang
  switch (commandline -t)
  case "!"
    commandline -t $history[1]; commandline -f repaint
  case "*"
    commandline -i !
  end
end

function bind_dollar
  switch (commandline -t)
  case "!"
    commandline -t ""
    commandline -f history-token-search-backward
  case "*"
    commandline -i '$'
  end
end

function fish_user_key_bindings
  bind ! bind_bang
  bind '$' bind_dollar
end

@siteshwar URL linked to in https://github.com/fish-shell/fish-shell/issues/288#issuecomment-7869918 is broken. Could you edit your comment to fix it for people reading this still-open issue?

I edited his comment to point to where we host the FAQ now.

Now that fish has && and || (https://github.com/fish-shell/fish-shell/issues/4620) It's so close to being a full replacement of bash!

I'm really confused --

So the shell which declares its number 1 design goal is:

1. Everything that can be done in other shell languages should be possible to do in fish

...doesn't allow me to do one of the most commonly-used actions in every other shell?

The fish faq says users should instead

First press Up, then Home, then type "sudo ".

Man, what a stretch. How many users have hit the 'Home' button in the last six months for any reason? At that length, I could argue that automatically parsing commands from the man page isn't orthogonal enough from just opening up the damn man file and finding it yourself.

@andysalerno
the action you want to do is to execute the previous command with sudo prepended
and fish provides this functionality
although instead of entering
First type "sudo ", then press shift+1, then press shift+1
I do
ctrl+p, ctrl+a, type "sudo "
this works the same way in bash
now that you mentioned the faq I checked it again and somebody removed the part about this shortcut is now missing. (i created https://github.com/fish-shell/fish-shell/pull/3879 to fix this)
The reason this shortcut works is because it follows the readline library which is widely implemented or mimicked for line editing.
If I find the time I will create a pull request to add this to the faq
I can understand your frustration, using the Home key really isn't an option, as that key is in a different spot on many keyboards (e.g. all of my 3 keyboards).

thx for bringing this up again. I think it is important to have an easy (only modifier and letter keys, no home key , no arrow keys), fast ( not too many keystrokes) and _documented_ way of doing this.

awesome, I had no idea about ctrl+p! I like this even better than !!.

edit:
is there any other way to re-evaluate the last-run command? I find myself doing something like this a lot in bash:

> man -w fish  # prints location of man file for fish
/usr/share/fish/man/man1/fish.1

> less $(!!)

It's not as concise, but you can do this with eval and the history variable:

> man -w fish
/usr/share/fish/man/man1/fish.1

> less (eval $history[1])

But a better way to do this is using alt-p. Fish will pipe command outputs to less if you press alt-p after you type the command:

> man fish alt-p > man fish ^&1 |less;

i dont understand all of the anti-bang people.. there are so many instances where the bang stuff is laughably faster and more convenient than such "work arounds" as:
First press Up, then Home, then type "sudo ".

Bang commands are fucking magic once you get used to them.. I really hope the fish guys change their minds and add them back in. At LEAST a few of the good ones, like:

!!
!WORD
!WORD:p
!:n
!$
!^
!n:n
!:gs/MISTAKE/FIX
!#
!#^

Fish is pretty nice though, keep up the good work

Is there a supported way to execute the last command in history? I don't mind having to type it out, because I'm just using a macro to call the command in my other tmux window. But my usecase is that I just want to be able to do something like this:

$ echo "hi"
hi
$ x
hi
$ x
hi

I tried eval $history[1], but the second call explodes. That's the useful thing about the !! command. The one thing that's not really acceptable is using the arrow keys. Is there any command in fish that supports this use case?

The best I can think of right now is to use a leading space which prevents adding it to history:

$ function x ; eval $history[1] ; end
$ echo "hi"
hi
$  x    # note leading space
hi
$  x
hi

it's certainly not great I know. In this simple case however, the history-search-backward binding seems like it should be sufficient? That's arrow keys by default but you can add or change it.

Oh! That's a good idea, I didn't think about using the leading space. I'll have to try that.

Update:
That worked perfectly for my usecase. $ eval $history[1] #note extra leading space

This is likely to be the reason I cannot stay with Fish. While it is elegant and simple, it's lack of standard shortcuts for many things makes it non-productive:

First item. I want to repeat the previous command with sudo:

sudo !!

1) Hit the Up button
Problem, I have to reach for the Up button on may keyboards. About one-quarter of the time I miss it and then must look down at the keyboard to see exactly where it is and probably fix whatever the miss messed up. Then...
2) Hit Home or Ctrl-A
Home is another stretch and is located in so many diverse locations on different keyboards I will have to look for it. Ctrl-A comes more naturally so that works I guess.
3) Type 'sudo ' and hit return.
Ok, that's only seven keystrokes, but one of them is prone to error and causing my workflow to be interrupted at a large percentage of the time.

Next item, I want to cat the previous last argument:

ls -l /etc/network/iptables
cat !$

Or with fish:
1) Type 'cat '
2) Alt-Up
Ok, again this is not very natural. I rarely hit the Alt key and more than often hit the Super (Cmd/Windows/whatever) key next to it which changes the context I'm in altogether.

Fish needs better history expansion like all of the major contenders. Its lack is a reduction in productivity for high end users.

@wrecklass Because I couldn't get sudo !! out of my fingers, I wrote https://github.com/nyarly/fish-bang-bang, which I'll suggest to you.

Also, IIRC, fish ships with Alt-S for "prepend sudo" which will work for the last history entry if your command is currently empty, the same way that Alt-P will append | less.

And just because it keeps coming up, ("i.e. why doesn't !! work in fish the way it does bash") I want to relate: the special treatment of the ! character was one of the things that drove me from bash. As a long time fish user, I'd be very disappointed if that behavior were brought over to appease a vocal minority.

sudo !! is such a standard thing. I understand that fish is trying very hard to get away from ancient bash syntax, but by pushing away legitimately useful features in favour for personal philosophy, I think we halt progress.

@ClickSentinel, for this very reason I quit using fish and stick to bash. :smiley:

Our current plan is to implement global abbreviations, which can then also support "!!" et al. See #5003 for that.

I'm closing this issue in favor of that as it has run its course.

@tukusejssirs and @ClickSentinel, have you tried adding the code at https://github.com/fish-shell/fish-shell/issues/288#issuecomment-306212011 to your config?

I've had a variant of that for several years (modified from an issue (this issue, maybe?) in my config, and it's one of the pieces of shell ergonomics I rely the most on. (Also, especially with the additional case I have in my variant to do an upward token search using the string prior to the !$, it's one of the things I'm most annoyed to not have when working in bash.)

My variant (or maybe I stole the variation...can't remember :man_shrugging: ):

function bind_bang
    switch (commandline --current-token)[-1]
    case "!"
        # Without the `--`, the functionality can break when completing
        # flags used in the history (since, in certain edge cases
        # `commandline` will assume that *it* should try to interpret
        # the flag)
        commandline --current-token -- $history[1]
        commandline --function repaint
    case "*"
        commandline --insert !
    end
end

function bind_dollar
    switch (commandline --current-token)[-1]
    # This case lets us still type a literal `!$` if we need to (by
    # typing `!\$`). Probably overkill.
    case "*!\\"
        # Without the `--`, the functionality can break when completing
        # flags used in the history (since, in certain edge cases
        # `commandline` will assume that *it* should try to interpret
        # the flag)
        commandline --current-token -- (echo -ns (commandline --current-token)[-1] | head -c '-1')
        commandline --insert '$'
    case "!"
        commandline --current-token ""
        commandline --function history-token-search-backward


    # Main difference from referenced version is this `*!` case
    # =========================================================
    #
    # If the `!$` is preceded by any text, search backward for tokens
    # that contain that text as a substring. E.g., if we'd previously
    # run
    #
    #   git checkout -b a_feature_branch
    #   git checkout master
    #
    # then the `fea!$` in the following would be replaced with
    # `a_feature_branch`
    #
    #   git branch -d fea!$
    #
    # and our command line would look like
    #
    #   git branch -d a_feature_branch
    #
    case "*!"
        # Without the `--`, the functionality can break when completing
        # flags used in the history (since, in certain edge cases
        # `commandline` will assume that *it* should try to interpret
        # the flag)
        commandline --current-token -- (echo -ns (commandline --current-token)[-1] | head -c '-1')
        commandline --function history-token-search-backward
    case "*"
        commandline --insert '$'
    end
end

function fish_user_key_bindings
    bind ! bind_bang
    bind '$' bind_dollar
end

Thanks @scooter-dangle this works even better than #228

There are cases where Fish will provide more idiomatic way to do some action:

LANG=C :
fish: Unsupported use of '='. To run ':' with a modified environment, please use 'env LANG=C :…'

Maybe !! could also, by default, generate such a message suggesting an alternative more idiomatic way to do it?

However the proposal should be just as efficient. Compare:

  • sudo !!<enter>, all from the base position of hands on the keyboard
  • <up><home>sudo <enter>, as suggested in Fish’s FAQ which seems requiring more or less moves of hands depending on where and are on the keyboard.
  • <C-p><Ca>sudo <enter>, accessible without moving hands , which also works with defaults of Bash, ZSH and maybe more, so users can adopt new habit which is a portable when they have to deal with other shells.

For !$, suggesting <alt-.> seems fine, doesn't it?

@psychoslave Please stop commenting on all the old issues, especially closed ones.

Also locking this one. It has been answered about fifty times over.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

luc-j-bourhis picture luc-j-bourhis  ·  3Comments

andrewhowdencom picture andrewhowdencom  ·  3Comments

krader1961 picture krader1961  ·  3Comments

rwz picture rwz  ·  3Comments

gawells picture gawells  ·  3Comments