Fish-shell: && doesn't work

Created on 19 Jun 2012  ·  98Comments  ·  Source: fish-shell/fish-shell

cd .. && pwd returns the following error:

fish: Expected a command name, got token of type 'Run job in background'. Did you mean 'COMMAND; and COMMAND'? See the help section for the 'and' builtin command by typing 'help and'.
cd .. && pwd

Obviously I can use the COMMAND; and COMMAND syntax, but it would be nice if fish supported this.

Thanks!

enhancement

Most helpful comment

|| would also be nice.

All 98 comments

|| would also be nice.

I just want to express an alternative opinion.
'||' and '&&' are the syntax elements, while 'and' and 'or' are simple commands. This approach is simple and clean.
I think that it's not a good idea to introduce additional syntax elements to implement already existing capability.
It's different from bash, but it's ok.

I'd also vote for having && and || as this is commonly used syntactical sugar and would be really nice to have w.r.t the usability.

+1 to maxfl's suggestion.

+1 to maxfl's suggestion.

Ruby also has and and or keywords.

+1

I have no objection if someone wants to take this on.

-1 to implementing &&/|| and +1 to maxfl. And I don't really see the reason why it should be implemented. It's not syntactic sugar, in bash it is just the syntax and in fish this syntax doesn't exist. It would also be really useless as syntactic sugar as ';or' takes exactly as many keystrokes as '||'.

I like the simplicity of and and or commands, but one problem is that they make if statements weird. For example:

if begin ; [ a = b ] ; and [ c = d ] ; end
   ...

compared to

if [ a = b ] && [ c = d ]
    ...

Anyone have ideas for improving that?

if test a = b -a c = d # or [ ] syntax, but I didn't particularly liked it
    ...

But while that works for test builtin, it doesn't work anywhere else. Let's assume that builtins a and b should return true to run c and d. Then, we could write it like that.

and b
and begin
    c
    d
end

Or with block syntax and if.

if begin
        a
        and b
    end
    c
    d
end

Or make nested conditonal.

if a
    if b
        c
        d
    end
end

Then again, I'm not sure how often this hack is needed, but searchco.de says that && is mostly used with test builtin (or for side-effects).

I'm not really sure if fish would need another syntax useful just in rare cases. It feels already like [, except that one had to stay because it's binary anyway (just like test). It doesn't feel particularly fishy, but it's here, because it's binary (unlike [[ which I'm glad fish doesn't support).

Having two syntaxes (or in this case, even three) would be rather confusing. Which syntax should I use?

if a
    b
end
a; and b
a && b

It looks like && is shortest, except that's half of truth. Typing ;and takes exactly the same number of keystrokes (because & involves Shift). Adding && would encourage adding adding additional syntax (then again, and is parsed sort of special way, because you can insert keywords like begin after it, and they work).

The problem is that we have ; and and ; or syntaxes - the new syntaxes would feel like duplicates. They are already sort of magical (they aren't just a shell script function that checks $status variable), so I wouldn't have problem if they would be replaced with && and ||.

I'm not sure what to think. I think there should be just one syntax to do it. I like ; and and ; or, but I wouldn't have problem with && and ||, provided that old syntaxes would be removed. But considering backwards compatibility, I would prefer to stay with ; and and ; or. Perhaps if begin would be shorter (like braces in bash)... but that wouldn't feel fishy.

Why not support a builtin [[ like bash does? It would be faster (see: builtin echo) and it would support && and || operators within the [[ and ]] to emulate the and/or behaviour.

Because [[ isn't actual command, it only exists in bash. The problem is that [[ is just like [ (mostly), except it does have special parsing rules to avoid needless quoting. See the law of orthogonality and the law of discoverability to see why it isn't in fish.

In fact, I'm almost sure [ wouldn't exist because of the law of discoverability and the law of orthogonality, but it does because it's binary in /usr/bin. test is more fishy, because you can discover it using tab completion. But blocking binaries would be rather arbitrary, so might as well implement it as builtin.

For example, in bash [[ automatically quotes all strings.

[[ abc = $variable ]]

But that isn't needed in fish because the whitespace splitting behavior doesn't exist. Also, it automatically quotes &&, ||, <, and > because it's syntactic feature. In fish, the && is -a, || is -o. < and > don't even exist.

It also does have =~ operator. In fish, you can use quiet grep for that.

echo $ANSWER | grep -qE '^y(es)?$'

Also, bash has < and > operator. They check whatever string is greater or lesser than other string. This is rarely useful.

And, when using = and != operators you can use globing syntax, just like you would use regular expressions. Again, use quiet grep for that.

[[ abc = a* ]]

Also, the [[ is defined to have -e, -nt, -ot, -ef, and !, but fish already has these in test builtin.

Adding [[ would just add another keyword, that wouldn't be really discoverable, and wouldn't really add much functionality. The only interesting functionality is =~, but quiet grep (or Perl) can do the same thing, without adding new syntax. Besides, we shouldn't add everything to shell. I don't think we need to have regular expression builtin.

Well, I didn't necessarily mean that we should reimplement bash's [[ builtin, but rather, it should be introduced as a syntactical construct with its own properties. I mean, fish shares a lot of keywords and constructs that look like bash's, but they're different, so, I don't see how this wouldn't be that way.

Although, based upon what you said, perhaps implementing our own test would be a better construct, although, renamed to something like cond, like how fish implements a math builtin instead of expr. We could even go to the point of implementing things like < and >, however, using natural (numeric) sorting instead of lexicographic sorting, so that it would work on numbers as well as strings.

@Undeterminant builtin [[ vs builtin [ vs /bin/[ subtle differences are already confusing in bash; please let's not make it worse by adding bash-[[ vs fish-[[ subtle differences!
Also, the spirit of bash's [[ violates the law of orthogonality — it "fixes" and/or but only for specific conditions the construct supports.

It's ironic that bash's if commands...; then ...; fi would easily admit fish's and/or between if...then. But we don't want a "then". Perhaps the thing to fix is making begin...end easier.

Here is a weird idea (I'm not at all sure I like it):

if (foo; and bar)
  ...
end

Currently (...) at start of command is illegal; the natural reading is execute this and use the output as command name but we require eval for that.
If we're confident we'll never allow that reading directly, we could re-purpose bare (...) to mean begin ...; end.
Not capturing stdout in this position is inconsistent — but very compact and clean looking.

What I hate about it is that if (...) begs for yet another meaning: do capture the output and assign it some boolean interpretation. E.g. if (math 2 \< 4)math doesn't return a status, it prints 0 or 1 to stdout. One important use would be if $saved_bool.
I don't think this is practical — there is no single right assignment of boolean meanings to strings, especially since 0/1 are hopelessly confused. But when you you first encounter if (...), it seems a reasonable guess.

For compatibility sake, please provide the common operators that are found in bash and zsh. The easier it becomes for new users to copy and paste most of their previous shell functionality over, the better the uptake rate of fish will be. I am finding that while I like fish better in many regards, some of these silly little hurdles are just making it more of a hassle than a benefit when coming over with over 15 years of shell customizations that really shouldn't be a problem to support.

I honestly don't see && as a "silly little hurdle." It wouldn't be difficult to learn if you treated fish as its own language instead of a "replacement shell" like zsh is. If you read the tutorial on how fish works from start to finish, I don't think that's difficult to understand.

Having something like command1; and command2 makes it more clear that the and is a conditional execution of command2 and not just some joining between command1 and command2.

But @Undeterminant, you're not seeing it as most people see it at first when they initially discover it, etc. Let that feeling or level of proficiency come with time. For example, I brought hundreds of lines over from old legacy config and honestly I don't have time to immediately convert everything over that needs to be converted. I'm finding oddities wherein there is no line number returning from fish regarding where my problem is within my old configs.

I can understand your perspective, but it is biased with regards to someone who wants to make the change and would like to do it over time vs passing over a big hump. Please don't misunderstand, if I didn't care about fish and thus try to give input, I'd certainly just hop back over to zsh right now. I feel that this kind of thought and reasoning is important to open the gate a bit further for would be newcomers.

So yes, you are right about readability, but let's let the old work and give a warning rather than an error to wean people over.

@ylluminate You're fooling yourself. Even if fish supported && you still couldn't just use your config from bash or zsh, because there's more differences to the scripting syntax than just the logical operators.

Exactly. fish is functionally different, which means that you shouldn't just copy-paste the code over directly. If your code is written in bash, unless there's an explicit need to convert it into fish, just keep it as bash; they're separate languages.

So
if I do
command1 && command2
in fish it suggests that instead I type
command1; and command2

Do someone really believe that (in the command line) the second option is cleaner, more futuristic and easier to type?

It is cleaner, because it builds upon the existing syntax for executing commands, instead of introducing new syntax.

Consider in bash:

foo & bar # executes foo in the background, then bar in the foreground
foo && bar # executes foo in the foreground, then executes bar if foo has a zero exit status
foo & bar && baz & qux # left as an exercise for the reader

This is the sort of nonsense we hope to avoid by using commands instead of special syntax.

This design decision definitely incurs a cost, especially in if statements, which we should try to reduce. But we should approach it from a position of "let's find ways to improve fish within its design philosophy" rather than "let's find ways to make fish more like other shells."

Your code does the following, right (assuming that you can use & with blocks)?

foo &
if bar
    baz
end &
qux

For me, it seems contrived. Also, if & syntax is so confusing, why not make background builtin, or something?

I think that is indeed an argument for making background a builtin, though I'm not proposing we actually do it.

The problem with ; and and ; or is they interact poorly with pipes. condition; and echo stuff | grep stuff does not work, while condition && echo stuff | grep stuff does. I see no way around this issue except to add special syntax.

and echo stuff | grep stuff seems to work for me. Can you elaborate on how it's broken?

@ridiculousfish here's the case that I discovered when trying out fish. The or expression is true outside the if statement but (appears to be?) false when used in an if statement. Unless I am missing something here?

root@smogon-dev ~# test 0 = 1; or echo stuff | grep stuff
stuff
root@smogon-dev ~# echo $status
0
root@smogon-dev ~# if test 0 = 1; or echo stuff | grep stuff
                       echo Success
                   else
                       echo Failure
                   end
Failure

Compare with bash:

~/smogon/bin$ [[ 0 == 1 ]] || echo stuff | grep stuff
stuff
~/smogon/bin$ echo $?
0
~/smogon/bin$ if [[ 0 == 1 ]] || echo stuff | grep stuff; then
> echo Success
> else
> echo Failure
> fi
stuff
Success

In this case:

if test 0 = 1; or echo stuff | grep stuff

it might be clearer if it was written like so:

if test 0 = 1
    or echo stuff | grep stuff
    ...

The or statement is in the if body! Since booleans are commands, they don't have special precedence.

You can use begin/end as "parenthesis":

if begin; test 0 = 1; or echo stuff | grep stuff ; end
    ...

So I think it's not related to pipes, but instead to precedence.

As you can see in this bug, there's a lot of opinions about whether fish should support boolean operators :)

so, thanks guys fo the energy put in here.

My intention isn't bring the discussion about which stuff is better or not. It's just a practical thing.
As most of you, I have my own scripts to run. Even in very simple things the && condition guarantees the command success (exit 0), while ";" just go ahead without caring whatever happened. Right? So, they are not the same at all. I hope we all agree on this. :)

So, I have my scripts and none of them works with fish. Yes, I use a lot &&. But this also happens with any peace of code over the net with bash, generally using && too. To make it short, the thing is that I can't use my codes anymore! :D

I was wondering... Is it possible to stablish a function or definition in the fish config file in order to fix this behaviour ONLY (meaning just &&) and turn it back as is in traditional bash? Is it possible? Plausible? Doable?

Again, I agree that the use of "and" may be nicer, clearer. But it doesn't work with my code or, in general, with any above-beginner bash code. So instead of changing the already written code replacing && (... won't happen... and most likely wont' work) it would be easier to allow fish to understand traditional bash if someone wants, or needs, no?

All the best guys, and thanks again for the effort!!

Have we ever gotten closer to full on compat with bash / zsh scripts? I gave up on fish as per my earlier comments because I'm not going to take the time to convert all of the infrastructure that I have built up already in my shell environments across so many workstations and servers. Would love to give it another try if we're closer. Otherwise I'll just keep peddling along without it. Thanks.

@rhoconlinux Changing Fish to support && will not make your bash scripts work. There's a lot more differences than that.

I didn't understand your answer ^_^
Is it possible to define an alias or a function for the && problem?

@rhoconlinux No, you'd have to modify the Fish parser (in C++). My point is, if Fish did support &&, your Bash scripts would still fail to run, because of all the other differences between Fish and Bash.

@kballard ouch! hmmm.... I'm so sad to hear this. I got it now. Super clear. :)

Thanks for the super-fast feedback. I'll use fish for a nicer everyday experience and will keep a bash terminal for my stuff then.

Cheers! :+1:

If && is a concern, it's easy to deal with it.

sed 's/&&/; and /' ~/.bashrc > ~/.config/fish/config.fish

But it's unlikely this will be the only thing you will need to do. What you mean is an implementation of #522.

I'd say the main motivation for && compatibility is not converting existing
codebases but copy-paste from the internet.
E.g. I see some sudo add-apt-repo... && sudo apt-get update && sudo apt-get install ... snippet, want to just copy-paste but it fails.

This is not something we can ever solve by education — there will be always
snippets to paste, and they'll use standard [ba]sh syntax.
And && is by far the #1 problem I'm bumping into.
(export FOO=bar is #2 but can be solved by function export; set -x -g (echo $argv | sed 's/=/\n/'); end.)

It's not a deep problem though: I've gotten into the habit of typing
bash, pasting, Ctrl+D.
Hmm, I'm tempted to add a keyboard shortcut to run current/previous
commandline under bash.

My two cents on the issue:
I am working on a deployment tool (https://github.com/andres-montanez/Magallanes) that makes shell command calls, and the majority of the users and contributors will be using a bash shell. So of course, here and there '&&' are used to chain commands together.

There's obvious workarounds (like passing all commands explicitly to bash)
But that brings a number of pain points. For one, there can be weird side effects (i.e. the PATH and other variables are not defined the same way), it means hacking some core parts of the tool for that sole purpose, and I need to do merge my fixes from version to version every time I update.

&& is only one of many bash features that fish isn't compatible with, but it might be the most basic and wildly used one. For instance the tool above is in PHP, so anything middy complex is done in PHP and only basic system commands are passed to the shell. Hitting syntax incompatibilities with such a minimal use case is frustrating.

I completely agree with what @cben said && is in my opinion a special case because there are hundreds of code snippets online where the only difference between copy and paste or not is the support for &&.
While I do agree it is important to take into consideration how fish goes forward in terms of syntax, etc. I do think that this is something that would make so many developer's life's who use or want to use fish much easier. I know it just seems like a simple replace && with ; and but it is always annoying and makes it feel a bit awkward when I tell other people how amazing fish is but then I have to fiddle around and change some keywords, they usually wonder why I have to change such a simple thing. Then I have to defend fish and that is not making a great case to promote fish.
Sometimes it really is the most simple things that can have the biggest impact.
So in short, I am all for allowing the bash && syntax.

I agree with what @Globegitter and @cben said. A vast amount of scripts and programs depends on && and ||. I use fish shell on my workstation and it would be nice to have cross-shell compatibility when writing scripts.

Given that there are a lot of significantly differences between Fish scripting and Bash scripting beyond just && and ||, why do all of you commenting here believe that making Fish accept && would be in any way meaningful? If you need to run a snippet of Bash code, just run bash, execute the snippet, and then exit back to Fish.

I think many folks would like to "convert" to fish, but have infrastructure to bring over. I, for example, have 15 years of shell scripts on just my workstation (not to mention other servers that I'd like to run fish on as well) that I don't want to have to spend the time converting. I don't mind poking around and making things work the "the fish way" over the course of several months, but there is no way I can make that kind of an initial investment in my workflow for changing to another shell.

I believe that fish would have a significant influx of users if the general compatibility issue were address, but it does not appear that this is something of a priority for the project from what others who work in the development of fish are saying. I keep my eyes on this for this very reason as I'd eventually like to move over to fish if the landscape changes in this regard.

With regards to just running bash or zsh as needed, that would be a significant amount of running those shells in my case and it's just silly to go down this path otherwise.

@kballard I have an ideal workflow for 95% of the scripting I do with fish: cmd+C a very simple script from another source, cmd+tab to iTerm, cmd+v and Enter. That is all I want to do, no extra bash or whatever, nothing else. Most of the time the only thing that stops these scripts from working with this exact workflow is, that there is a && present. So that is the reason why for me and I assume most of the others here resolving this issue would be very useful.
If you don't experience that yourself, believe me it does add up and really sticks to your mind if your workflow gets interrupted so often.

I love fish for everything else and that is why I couldn't switch back, but this simple thing is just what keeps it from being awesome for me and I assume some of the others here.

Here you go. Once PR #1633 lands, you can take the following and save it in ~/.config/fish/functions/fish_user_key_bindings.fish:

function handle_input_bash_conditional --description 'Function used for binding to replace && and ||'
    # This function is expected to be called with a single argument of either & or |
    # The argument indicates which key was pressed to invoke this function
    if begin; commandline --search-mode; or commandline --paging-mode; end
        # search or paging mode; use normal behavior
        commandline -i $argv[1]
        return
    end
    # is our cursor positioned after a '&'/'|'?
    switch (commandline -c)[-1]
    case \*$argv[1]
        # experimentally, `commandline -t` only prints string-type tokens,
        # so it prints nothing for the background operator. We need -c as well
        # so if the cursor is after & in `&wat` it doesn't print "wat".
        if test -z (commandline -c -t)[-1]
            # Ideally we'd just emit a backspace and then insert the text
            # but injected readline functions run after any commandline modifications.
            # So instead we have to build the new commandline
            #
            # NB: We could really use some string manipulation operators and some basic math support.
            # The `math` function is actually a wrawpper around `bc` which is kind of terrible.
            # Instead we're going to use `expr`, which is a bit lighter-weight.

            # get the cursor position
            set -l count (commandline -C)
            # calculate count-1 and count+1 to give to `cut`
            set -l prefix (expr $count - 1)
            set -l suffix (expr $count + 1)
            # cut doesn't like 1-0 so we need to special-case that
            set -l cutlist 1-$prefix,$suffix-
            if test "$prefix" = 0
                set cutlist $suffix-
            end
            commandline (commandline | cut -c $cutlist)
            commandline -C $prefix
            if test $argv[1] = '&'
                commandline -i '; and '
            else
                commandline -i '; or '
            end
            return
        end
    end
    # no special behavior, insert the character
    commandline -i $argv[1]
end

function fish_user_key_bindings
    bind \& 'handle_input_bash_conditional \&'
    bind \| 'handle_input_bash_conditional \|'
end

This binds & and | and makes it so typing && or || automatically translates it into the appropriate ; and / ; or, thus allowing you to paste in lines of the form command one && command two and have it work.

Note that until PR #1633 is accepted, this script will work for _typing_ && / || but doesn't work for pasting it.

@kballard Wow, that is great news! Thank you so much.

Look like this doesn't actually handle search mode properly (not sure about pager mode; I'm unclear as to what exactly comprises that). Hitting & or | during search terminates the search. That said, I don't know how to handle that.

I updated the script with a bugfix for typing | or & in a multi-line command.

@kballard I saw that the PR has landed. So the script you pasted above should make && work with the latest master?

Yeah it should. Try it out and let me know if you have any issues.

-Kevin

On Sep 1, 2014, at 1:14 AM, Markus Padourek [email protected] wrote:

@kballard I saw that the PR has landed. So the script you pasted above should make && work with the latest master?


Reply to this email directly or view it on GitHub.

I tried this script with the new 2.1.1 version released a few days ago, and it doesn't seem to work. Will I have to wait until 2.2? Thanks!

@pragmattica: 2.1.1 was security bugfix release. It didn't add any new features.

@xfix: OK. Thanks for letting me know. Can't wait until 2.2!

AWESOME :+1:

Any running changelog available? It appears that the root CHANGELOG is quite out of date.

@pragmattica Now that fish 2.2 is out I tried that on ubuntu 14.04 and echo 'test1' && echo 'test2'; turns into echo test1 &; and echo 'test2';

Could there be an issue somewhere within the fish_user_key_bindings.fish? (I will try to look over it myself, but my fish/bash script knowledge is a bit shaky)

Edit: It does seem to work however when you type in a command with &&. It might be a bit nice if it would happen on the space after-wards but that is not really important. Great that half is working :)

-1 to weird symbols:

if [[ a = b ]]; and [[ b = a ]]; or [[ c = b ]]; then echo hello; and echo world; done

+1 to simple symbols:

if [[ a = b ]] && [[ b = a ]] || [[ c = b ]]; then echo hello && echo world; done

Aaahhh I think I know what the issue is @pragmattica the command commandline returns the buffer without any quotation-marks. Yep that seems to be it. So once https://github.com/fish-shell/fish-shell/issues/2210 is fixed the above script should be fixed as well.

Is this still begin considered?

+1
The symbols aren't weird, they're commonly used for logic in a wide number of programming languages.
Copy and paste compatibility is a very real argument, and one I support.

I'm going to be the bad guy and bring this discussion to a close. This will not be implemented. I'm especially bothered by the repeated statements about wanting to be able to "cut and paste commands from bash scripts". Even if fish supported && and || operators that would not ensure you could simply cut and paste statements containing those operators. We do not want to give the impression that you can do so. Honestly, if you want bash/zsh syntax and behavior you should use those shells (or an alternative that claims compatibility with them).

Most of the other ideas discussed should have their own issues. In particular there may be some merit to implementing some of the [[ ... ]] behaviors that were first introduced by ksh88 (not bash). But probably via enhancing the builtin test command or a new command rather than by implementing those special tokens.

I am still very disturbed by this notion of not supporting zsh and bash. I can understand the logic, but I don't agree with it. I was very interested in fish some time ago, but moved back away due to this opposition. There may be some opportunity to fork the project here if there are enough of us, as developers, who would like to see this implemented. Sad to see this continued resistance which ultimately impedes the forward momentum and acceptance of such an interesting piece of work.

@ylluminate what do you have in mind by "supporting zsh and bash?" What level of compatibility would you like to see?

I am still very disturbed by this notion of not supporting zsh and bash. I can understand the logic...

I'm afraid I don't understand your logic for having fish support bash and zsh features. If you want Java why use C++? If you want Perl why use Python? As @ridiculousfish just asked, besides some syntactic sugar what else would fish have to support to make you happy? At what point does fish just become another bash clone?

P.S., I've been programming for a living since 1979 and using UNIX since the mid 80's. I've used so many shells I've lost count (e.g., Bourne, Csh, Ksh88, Ksh93, Bash, Zsh, Xonsh). Switching shells always requires work (which is why I don't do it more than once every five years or so). When I do so I don't complain that my new shell doesn't run all my legacy scripts without change.

@krader1961 Nice speech. In My Humble Opinion...

I wouldn't do scripting in fish all the time as (followed by orders):

  1. fish's script is slow (slower than mksh)
  2. some weird syntax (eg. ; and, ; or which looks ugly for me)
  3. not posix compatible (I can say the syntax is easy but still need time to learn)

Well, I wouldn't care about the 2 and 3 only if fish script is faster than other shells (eg. mksh, bash).

On the other hand, I use fish because:

  1. startup time is faster than zsh (but slower than bash)
  2. have a lot of features enabled by default (syntax highlight is the best)
  3. configurations is nice and simple (especially set -Ux)

@ylluminate Looking at this four-year-old issue, I can see how lazy/busy the people who want to enjoy fish like bash, are still lazy/busy. The eager ones have been making their own shell (such as magicant/yash, elvish/elvish, michaelmacinnis/oh, and me :-P). If you want to do things in an un-fishy way, you probably need an un-fishy shell.

If you really consider how to implement && || onto fish and make that not very un-fishy, then you may see that you are just expelling begin; end (see what commit 594b460ba2d8dca59a3bfd282397c5f33aa9da6f does, counter-;and or play double roles, verysomewhat ugly) and get only some little sugar.

@pickfire Can you elaborate on "fish's script is slow?" Are there any tests that lead you to that conclusion?

FWIW in my tests fish is faster than bash overall, by virtue of posix_spawn and its fast parser, though it may be slower in certain cases like alias.

ivan@alarmpi /tmp> echo 'echo test' > script
ivan@alarmpi /tmp> time bash script
test
0.02user 0.01system 0:00.03elapsed 96%CPU (0avgtext+0avgdata 2776maxresident)k
0inputs+0outputs (0major+149minor)pagefaults 0swaps
ivan@alarmpi /tmp> time mksh script
test
0.00user 0.00system 0:00.02elapsed 0%CPU (0avgtext+0avgdata 1420maxresident)k
480inputs+0outputs (2major+82minor)pagefaults 0swaps
ivan@alarmpi /tmp> time fish script
test
0.07user 0.01system 0:00.09elapsed 85%CPU (0avgtext+0avgdata 4204maxresident)k
352inputs+0outputs (2major+231minor)pagefaults 0swaps

@ridiculousfish Just imagine it is already very slow compare to the others for just a single echo.

@krader1961 @ridiculousfish IMO, once https://github.com/fish-shell/fish-shell/issues/2210 gets released this can be closed. There is a very easy solution that @kballard posted.

@pickfire Thanks for sharing that. That test is measuring startup overhead, not the time of echo. Startup time is very important, but you can't draw conclusions beyond startup time (which is mostly dependent on config.fish) from that test.

Here's a different micro benchmark:

> cat test.fish
for i in (seq 1000)
    ls > /dev/null
end

> time fish test.fish
        1.51 real         0.74 user         0.65 sys

> cat test.sh
for i in {1..1000} ; do
    ls > /dev/null
done

> time bash test.sh
        2.01 real         0.85 user         1.12 sys

here fish wins by a lot, but this is mainly measuring the difference of fork vs posix_spawn.

I think one conclusion here is that we need a comprehensive benchmark suite.

@ridiculousfish Here is it, the benchmark that you have gave me:

> cat test.fish
for i in (seq 1000)
    ls > /dev/null
end
> time fish test.fish
4.18user 4.04system 0:22.62elapsed 36%CPU (0avgtext+0avgdata 4360maxresident)k
96inputs+0outputs (1major+321041minor)pagefaults 0swaps
> cat test.sh
for i in {1..1000} ; do
    ls > /dev/null
done
> time bash test.sh
0.70user 1.62system 0:09.81elapsed 23%CPU (0avgtext+0avgdata 2844maxresident)k
0inputs+0outputs (0major+154100minor)pagefaults 0swaps
> time mksh test.sh
0.00user 0.01system 0:00.04elapsed 24%CPU (0avgtext+0avgdata 1780maxresident)k
752inputs+0outputs (3major+203minor)pagefaults 0swaps

The mksh seems to have an impressive speed.

@pickfire fish's ls is a function on Linux that does some other stuff to make the output nicer, which bash is not doing. If you want an apples-to-apples comparison on Linux, use command ls. (On Darwin, fish's ls wrapper is much thinner - I should have pointed that out but I forgot.)

The explanation for mksh's "impressive speed" in your test is that mksh does not have brace expansion, so it's only invoking ls one time. Obviously invoking ls once is much faster than invoking it 1000 times.

This shows that performance measurement is very tricky - it's very easy to measure something different than what you think you're measuring!

@ridiculousfish, is there any reason to use the ls function is fish script compare to command ls? The output isn't important during scripting.

Okay, this time I did what you told:

> cat test.fish
for i in (seq 1000)
    command ls > /dev/null
end
> time fish test.fish
0.66user 1.04system 0:08.08elapsed 21%CPU (0avgtext+0avgdata 4364maxresident)k
624inputs+0outputs (4major+113176minor)pagefaults 0swaps
> cat test.sh
for i in $(seq 1000) ; do
    ls > /dev/null
done
> time mksh test.sh
0.21user 0.65system 0:07.64elapsed 11%CPU (0avgtext+0avgdata 1884maxresident)k
0inputs+0outputs (0major+119632minor)pagefaults 0swaps
> time bash test.sh
0.15user 1.04system 0:08.66elapsed 13%CPU (0avgtext+0avgdata 2816maxresident)k
0inputs+0outputs (0major+150700minor)pagefaults 0swaps

At the end, mksh is still the fastest, now I find it impressive for fish to be faster than mksh. If fish has built-in parallel command execution support, I think it would have been the fastest of all.

That .66 user time is quite high. I speculate it's due to your config.fish contents - defining aliases and such. For the record, this is what I see on my Linux box (best out of 3 for each):

> time fish test.fish
0.15user 1.21system 0:01.33elapsed...
> time mksh test.mksh
0.12user 1.13system 0:01.24elapsed...
> time bash test.sh
0.18user 1.34system 0:01.47elapsed...

everyone is very close.

It might be interesting for any benchmark to incorporate a sort of "oracular shell" that just does a fork/exec or posix_spawn to some path command, so we can get a sense of how far we are from the maximum speed that the OS can deliver.

@ridiculousfish, my config.fish is very short:

# Solarized colors
test $SHLVL = 2
and sh $HOME/.config/base16-shell/base16-solarized.dark.sh &
set fisher_home ~/.local/share/fisherman
set fisher_config ~/.config/fisherman
source $fisher_home/config.fish

Well because probably it uses fisherman, not sure if it will slow it down. @bucaran is the nice guy for this. This time, I emptied the config.fish (best result of 5 retries):

> time fish test.fish; time mksh test.sh; time bash test.sh
0.62user 1.09system 0:07.28elapsed 23%CPU (0avgtext+0avgdata 4396maxresident)k
0inputs+0outputs (0major+108240minor)pagefaults 0swaps
0.21user 0.62system 0:06.92elapsed 11%CPU (0avgtext+0avgdata 1888maxresident)k
0inputs+0outputs (0major+116674minor)pagefaults 0swaps
0.29user 0.81system 0:07.78elapsed 14%CPU (0avgtext+0avgdata 2780maxresident)k
0inputs+0outputs (0major+145628minor)pagefaults 0swaps

@ridiculousfish @pickfire Can you chat in another issue so I can block your messages?

Yes, sorry for spamming this issue. @pickfire if you'd like to follow up some benchmarks that you think are especially interesting or relevant, please feel free to open another issue.

About bash and zsh compatibility, I personally don't expect a full compatibility, but with every bit of compatibility added fish can become more popular.
Even without taking scripting into account, && and || are important because they are very commonly used for chaining commands. As an example I still have problems with #2292. I know it can be considered as not a fish bug, but the fact is that improving fish compatibility would fix it.

I'm with maxfl here, :-1: for having || and && as part of the fish syntax, if this is supposed to be a popular vote.

Fish is not bash (or zsh). There is no compelling reason to implement && and || other than for compatibility with those shells. Compatibility with the status-quo is an anti-goal of this project.

@Nodd: Your reference to issue #2292 is actually an example of why fish should not implement that syntax. Doing so would be another step towards making people think fish is a POSIX (bash, zsh, etc.) clone. That would just create other demands that fish behave like those shells in other areas.

For all the people that are looking for a solution, since fish 2.3 I can confirm there finally is a fully working solution.

Just do what above answer mentions:

Here you go. Once PR #1633 lands, you can take the following and save it in ~/.config/fish/functions/fish_user_key_bindings.fish:

function handle_input_bash_conditional --description 'Function used for binding to replace && and ||'
    # This function is expected to be called with a single argument of either & or |
    # The argument indicates which key was pressed to invoke this function
    if begin; commandline --search-mode; or commandline --paging-mode; end
        # search or paging mode; use normal behavior
        commandline -i $argv[1]
        return
    end
    # is our cursor positioned after a '&'/'|'?
    switch (commandline -c)[-1]
    case \*$argv[1]
        # experimentally, `commandline -t` only prints string-type tokens,
        # so it prints nothing for the background operator. We need -c as well
        # so if the cursor is after & in `&wat` it doesn't print "wat".
        if test -z (commandline -c -t)[-1]
            # Ideally we'd just emit a backspace and then insert the text
            # but injected readline functions run after any commandline modifications.
            # So instead we have to build the new commandline
            #
            # NB: We could really use some string manipulation operators and some basic math support.
            # The `math` function is actually a wrawpper around `bc` which is kind of terrible.
            # Instead we're going to use `expr`, which is a bit lighter-weight.

            # get the cursor position
            set -l count (commandline -C)
            # calculate count-1 and count+1 to give to `cut`
            set -l prefix (expr $count - 1)
            set -l suffix (expr $count + 1)
            # cut doesn't like 1-0 so we need to special-case that
            set -l cutlist 1-$prefix,$suffix-
            if test "$prefix" = 0
                set cutlist $suffix-
            end
            commandline (commandline | cut -c $cutlist)
            commandline -C $prefix
            if test $argv[1] = '&'
                commandline -i '; and '
            else
                commandline -i '; or '
            end
            return
        end
    end
    # no special behavior, insert the character
    commandline -i $argv[1]
end

function fish_user_key_bindings
    bind \& 'handle_input_bash_conditional \&'
    bind \| 'handle_input_bash_conditional \|'
end

@Globegitter

What about just?

function sudo_bang_bang --on-event fish_postexec
    abbr !! sudo $argv[1]
end

@brj Also putting that into ~/.config/fish/functions/fish_user_key_bindings.fish? That did not seem to do the trick for me.

You need to source this function. So, your options are to put it in your config.fish or put it inside ~/.conf.d/[name-dont-matter].fish.

@brj: Wrong issue for that function. This is about "&&" and "||", not "!!" - different things.

@faho lol, you are right. I always get a notification from that issue, so I thought this was another case of sudo !!.

@Globegitter Sorry for the confusion!

How about shipping the proposed work-around with fish as example, so users can conveniently copy it? The error message could easily be augmented to mention this. I keep on pasting the same one liner (from gerrit code-review) where && is used because it works in pretty much all shells (including windows cmd.exe besides unix-y shells). To me the above mentioned script is a productivity booster (I was typing bash -c "paste here" for a while...).

How do I do && ? Is there a resolution?

Same problem as above, I really can't find any way to do

gcc test2.c -o a.out && ./a.out

in fish.

gcc test2.c -o a.out; and ./a.out

@ivan tried that, but it gives "and" and "./a.out" as arguments to gcc.

//edit: By the way, I ended up using bash script, because this seems to be completely impossible in fish :(

gcc test2.c -lwlc -o a.out && (( sleep 2s; DISPLAY=:2 xterm ) & ./a.out)

I do this "completely impossible" thing quite often. Maybe try the ; and syntax in a clean fish environment? If it doesn't work, maybe something is up with your semicolons? Are you typing this into fish, or is it getting passed it from another program?

@ivan Typing it in.

Sorry for probably stupid question, but how do I do parentheses? I can't find a word about them in documentation and it's not exactly googleable topic :(

gcc test2.c -lwlc -o a.out && (( sleep 2s; DISPLAY=:2 xterm ) & ./a.out)

@kozec I think you're just having trouble grouping your items together here. What you need to use is begin like a left brace (that's how I think of it) and end like a right one.

gcc test2.c -lwlc -o a.out; and begin sleep 2s; env DISPLAY=:2 xterm; end & ./a.out

or indented:

      gcc test2.c -lwlc -o a.out
         and begin sleep 2s
             env DISPLAY=:2 xterm
         end & ./a.out

@floam Thanks, that almost works. But stuff before & is not sent to background :(

Minimal example:

begin sleep 2s ; env DISPLAY=:2 xterm ; end & Xephyr :2

sleeps 2s, then launches xterm, _then_ starts XServer for that xterm. xterm ofc crashes before XServer becames ready.

You will find you can work around it by this where you must, not pretty:

fish -c 'sleep 2s; env DISPLAY=:2 xterm' & Xephyr :2

When you have a large amount of existing scripts, for development, building and inside npm run-scripts it's not reasonable to switch, especially since some/most are not on fish. For the regular bash scripts with shebang and all it's not a problem of course.

The nice (or "nice") thing about && is that it works on the regular shells on not only Linux and Macs but also even Windows which is nice for quick setup of dev environments etc.

Not a deal breaker by any means, but kind of hard to see why adding it would be, either?

Well, this is a rather silly argument to have. Why not have different "modes" for the fish compiler? I.e., some sort of setting that you can enable wherein you can have "pure" fish stuff or you can have compatibility with bash-like shells along with fish-isms. You could even have different "levels" of compatibility for other shells so that you can have full compatibility, no compatibility, or something in between. I mean, at a basic level, you could at least implement some sort of source-to-source compiler for this. Or take bash code and translate it to an internal, intermediate representation for fish. You could do all kinds of things for translation in this regard. Correct me if I'm wrong, but I believe that the language-level concepts of bash et al. vs. fish aren't world-shatteringly different, right?

Anyway, that's my $0.02 on this issue that really seems silly because it's built on the premise of:

"pure fish"
XOR
"compatibility with bash / becoming another bash clone"

when there's nothing that prevents having middle-ground(s) between these two notions.

One ought to at least face the reality that bash-like shells are far more standard and popular than more esoteric shells such as fish so there really should be some sort of compatibility layer for any realistic chance of fish becoming a shell that's able to stand toe-to-toe with bash in terms of being widespread and not causing compatibility headaches.

...when there's nothing that prevents having middle-ground(s) between these two notions.

Nothing other than someone (or group of people) investing more than 2,000 hours (a person year) to write the necessary code. I look forward to reviewing pull-requests from you, @ylluminarious, to implement all of that. What you're proposing is equivalent to asking why can't someone make Python also interpret PHP. The answer is that theoretically you could have a compatibility switch to do so but why would you?

Implemented in #4620.

Was this page helpful?
0 / 5 - 0 ratings