New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide subshell like functionality #1439

Open
jorbsd opened this Issue Apr 28, 2014 · 34 comments

Comments

Projects
None yet
@jorbsd

jorbsd commented Apr 28, 2014

While fish avoids forking/execing, having subshell like functionality would still be very nice.

In bash/zsh:

pwd; (cd ; ; pwd;); pwd

should be in the first dir, change and do stuff, then show that you are back in the original did

This closure of shell state in the subshell (vars, directories, etc.) is very handy.

bash/zsh modern syntax uses $() to be similar to () in fish, both replacing ``

@Debilski

This comment has been minimized.

Show comment
Hide comment
@Debilski

Debilski May 15, 2014

I think the question is whether this is useful for anything other than cd dir; …. You might get similar behaviour with pushd, popd and set -l in beginend blocks and still retain the chance to escape the inner scope.

Debilski commented May 15, 2014

I think the question is whether this is useful for anything other than cd dir; …. You might get similar behaviour with pushd, popd and set -l in beginend blocks and still retain the chance to escape the inner scope.

@xfix

This comment has been minimized.

Show comment
Hide comment
@xfix

xfix May 15, 2014

Member
fish -c '
    echo totally a subshell
    echo no, seriously
    cd ..
'
Member

xfix commented May 15, 2014

fish -c '
    echo totally a subshell
    echo no, seriously
    cd ..
'
@Debilski

This comment has been minimized.

Show comment
Hide comment
@Debilski

Debilski May 16, 2014

@xfix: No colour highlighting though. ;)

Debilski commented May 16, 2014

@xfix: No colour highlighting though. ;)

@ghost

This comment has been minimized.

Show comment
Hide comment
@ghost

ghost May 29, 2014

I think that the benefit to a subshell would be to avoid forking to another process for performance reasons. Although, bash/zsh may just fork on subshells anyway. Syntax could be a modification to the begin command, i.e., begin --subshell.

ghost commented May 29, 2014

I think that the benefit to a subshell would be to avoid forking to another process for performance reasons. Although, bash/zsh may just fork on subshells anyway. Syntax could be a modification to the begin command, i.e., begin --subshell.

@jorbsd

This comment has been minimized.

Show comment
Hide comment
@jorbsd

jorbsd Jun 14, 2014

ridiculoufish should have most of my thoughts on this from the last year when I used fish on and off and kept only going back to modified zsh to get subshells back each time. I wish I had kept better notes each time, but at least I always let him know what I was up to and my thoughts on subshells at the time

jorbsd commented Jun 14, 2014

ridiculoufish should have most of my thoughts on this from the last year when I used fish on and off and kept only going back to modified zsh to get subshells back each time. I wish I had kept better notes each time, but at least I always let him know what I was up to and my thoughts on subshells at the time

@cben

This comment has been minimized.

Show comment
Hide comment
@cben

cben Aug 12, 2014

Contributor

pushd/popd are inferior because an error (or manual Ctrl+C) leaves you in the wrong dir.
A withd DIR COMMAND (not the best name) that reliably chdirs back would be very convenient
(in fact, given fish's Alt+Left/Alt+Right navigation, I don't see why we need pushd/popd/dirs at all. Especially in addition to dirh/prevd/nextd).
But then it can be useful with multiple commands, so it'd need to be a

withd DIR
    COMMAND
    ...
end

builtin, at which point the specialization to changing directories is silly, it should really be

subshell
    cd DIR
    ...
end

=> How about just adding cd -l that works in blocks?

Other use cases for subshells:

  • Temporary setting env vars. For one command env FOO=BAR COMMAND is very convenient;
    for multiple begin; set -l -x FOO BAR; COMMAND; ...; end works but is a bit awkward to remember (specifically -x is easy to forget - usually set does the right thing without it, but not set -l).
  • umask/ulimit. Very rare.
Contributor

cben commented Aug 12, 2014

pushd/popd are inferior because an error (or manual Ctrl+C) leaves you in the wrong dir.
A withd DIR COMMAND (not the best name) that reliably chdirs back would be very convenient
(in fact, given fish's Alt+Left/Alt+Right navigation, I don't see why we need pushd/popd/dirs at all. Especially in addition to dirh/prevd/nextd).
But then it can be useful with multiple commands, so it'd need to be a

withd DIR
    COMMAND
    ...
end

builtin, at which point the specialization to changing directories is silly, it should really be

subshell
    cd DIR
    ...
end

=> How about just adding cd -l that works in blocks?

Other use cases for subshells:

  • Temporary setting env vars. For one command env FOO=BAR COMMAND is very convenient;
    for multiple begin; set -l -x FOO BAR; COMMAND; ...; end works but is a bit awkward to remember (specifically -x is easy to forget - usually set does the right thing without it, but not set -l).
  • umask/ulimit. Very rare.
@ridiculousfish

This comment has been minimized.

Show comment
Hide comment
@ridiculousfish

ridiculousfish Aug 12, 2014

Member

cd -l seems totally brilliant to me.

Member

ridiculousfish commented Aug 12, 2014

cd -l seems totally brilliant to me.

@jorbsd

This comment has been minimized.

Show comment
Hide comment
@jorbsd

jorbsd Aug 12, 2014

I thought "subshell begin; ...; end;" was pretty damn good.

Maybe both for different uses?

Sent from my iPhone

On Aug 12, 2014, at 09:56, ridiculousfish notifications@github.com wrote:

cd -l seems totally brilliant to me.


Reply to this email directly or view it on GitHub.

jorbsd commented Aug 12, 2014

I thought "subshell begin; ...; end;" was pretty damn good.

Maybe both for different uses?

Sent from my iPhone

On Aug 12, 2014, at 09:56, ridiculousfish notifications@github.com wrote:

cd -l seems totally brilliant to me.


Reply to this email directly or view it on GitHub.

@c22

This comment has been minimized.

Show comment
Hide comment
@c22

c22 Aug 2, 2016

If I'm not mistaken, this problem kinda goes away if #563 is fixed, right?

c22 commented Aug 2, 2016

If I'm not mistaken, this problem kinda goes away if #563 is fixed, right?

@ridiculousfish

This comment has been minimized.

Show comment
Hide comment
@ridiculousfish

ridiculousfish Aug 2, 2016

Member

Maybe, maybe not, depending on how it's fixed. A subshell is both concurrent execution and independent state (multiple $PWDs, etc).

Member

ridiculousfish commented Aug 2, 2016

Maybe, maybe not, depending on how it's fixed. A subshell is both concurrent execution and independent state (multiple $PWDs, etc).

@d

This comment has been minimized.

Show comment
Hide comment
@d

d Aug 21, 2016

variables (exported or not) is a case where I'd love to have a subshell:

Scenario

begin --subshell
    docker-machine env aws-fast | source
    docker run --rm --volume /ccache ubuntu tar c /ccache
end | begin --subshell
    docker-machine env aws-slow | source
    docker run --rm --volume /ccache ubuntu tar x
end

Should be equivalent to the following Bash snippet:

(
eval "$(docker-machine env aws-fast)"
docker run --rm --volume /ccache ubuntu tar c /ccache
) | (
eval "$(docker-machine env aws)"
docker run --rm --volume /ccache ubuntu tar x
)

d commented Aug 21, 2016

variables (exported or not) is a case where I'd love to have a subshell:

Scenario

begin --subshell
    docker-machine env aws-fast | source
    docker run --rm --volume /ccache ubuntu tar c /ccache
end | begin --subshell
    docker-machine env aws-slow | source
    docker run --rm --volume /ccache ubuntu tar x
end

Should be equivalent to the following Bash snippet:

(
eval "$(docker-machine env aws-fast)"
docker run --rm --volume /ccache ubuntu tar c /ccache
) | (
eval "$(docker-machine env aws)"
docker run --rm --volume /ccache ubuntu tar x
)
@ridiculousfish

This comment has been minimized.

Show comment
Hide comment
@ridiculousfish

ridiculousfish Aug 27, 2016

Member

The begin --subshell syntax is pretty clever

Member

ridiculousfish commented Aug 27, 2016

The begin --subshell syntax is pretty clever

@alphapapa

This comment has been minimized.

Show comment
Hide comment
@alphapapa

alphapapa Sep 20, 2016

My two cents: I'd love to have a withd-style command. My situation: running a shell in Emacs, I want to run make in the parent directory, but end up back in the current directory, without having to explicitly save and restore the directory. This is easy in Bash with subshells, but not in Fish.

A withd could be put together as a script now, one that takes a string, but it would preclude syntax highlighting. But this is still pretty handy:

function withd
    cd $argv[1]
    or return 1

    fish -c $argv[2]
    cd -
end

alphapapa commented Sep 20, 2016

My two cents: I'd love to have a withd-style command. My situation: running a shell in Emacs, I want to run make in the parent directory, but end up back in the current directory, without having to explicitly save and restore the directory. This is easy in Bash with subshells, but not in Fish.

A withd could be put together as a script now, one that takes a string, but it would preclude syntax highlighting. But this is still pretty handy:

function withd
    cd $argv[1]
    or return 1

    fish -c $argv[2]
    cd -
end
@nrnrnr

This comment has been minimized.

Show comment
Hide comment
@nrnrnr

nrnrnr Oct 11, 2016

Either cd -l or withd would work for me.

nrnrnr commented Oct 11, 2016

Either cd -l or withd would work for me.

@stephane-chazelas

This comment has been minimized.

Show comment
Hide comment
@stephane-chazelas

stephane-chazelas Dec 14, 2016

subshells are also useful to isolate a portion of code with regards to signal delivery (think SIGPIPE for instance).

Note that ksh93 fakes subshells without forking (so holds a copy of all original resources (umask, cwd, traps, aliases...) which are restored when the subshell exits). You may want to have a look at that for your cd -l, alias -l, umask -l, function -l (like for cd -l open the current directory on a fd with O_CLOSEEXEC, and fchdir to that latter).

stephane-chazelas commented Dec 14, 2016

subshells are also useful to isolate a portion of code with regards to signal delivery (think SIGPIPE for instance).

Note that ksh93 fakes subshells without forking (so holds a copy of all original resources (umask, cwd, traps, aliases...) which are restored when the subshell exits). You may want to have a look at that for your cd -l, alias -l, umask -l, function -l (like for cd -l open the current directory on a fd with O_CLOSEEXEC, and fchdir to that latter).

@ridiculousfish

This comment has been minimized.

Show comment
Hide comment
@ridiculousfish

ridiculousfish Dec 15, 2016

Member

That's kind of amazing that ksh93 does that. Thanks for the tip, I definitely want to take a look.

Edit for anyone else struggling to find ksh93 sources, https://github.com/att/ast seems to have them.

Member

ridiculousfish commented Dec 15, 2016

That's kind of amazing that ksh93 does that. Thanks for the tip, I definitely want to take a look.

Edit for anyone else struggling to find ksh93 sources, https://github.com/att/ast seems to have them.

@aaronjwood

This comment has been minimized.

Show comment
Hide comment
@aaronjwood

aaronjwood May 4, 2017

If fork/exec was implemented, at least optionally, parallelism could also be restored. For example:

sleep 10 &
sleep 10 &
sleep 10 &
sleep 10 &
wait

wait doesn't block in fish which I think is a real killer since everyone writing scripts with this kind of code will rely on being blocked. Is there a fish-specific way of doing this, similar to how you have an emulated way of doing subshells?

I'm guessing everything runs in some sort of event loop in fish due to wait returning immediately...?

Possibly related to #3952

aaronjwood commented May 4, 2017

If fork/exec was implemented, at least optionally, parallelism could also be restored. For example:

sleep 10 &
sleep 10 &
sleep 10 &
sleep 10 &
wait

wait doesn't block in fish which I think is a real killer since everyone writing scripts with this kind of code will rely on being blocked. Is there a fish-specific way of doing this, similar to how you have an emulated way of doing subshells?

I'm guessing everything runs in some sort of event loop in fish due to wait returning immediately...?

Possibly related to #3952

@alphapapa

This comment has been minimized.

Show comment
Hide comment
@alphapapa

alphapapa May 5, 2017

@aaronjwood Unfortunately there is no wait in fish:

$ type wait
type: Could not find “wait”

alphapapa commented May 5, 2017

@aaronjwood Unfortunately there is no wait in fish:

$ type wait
type: Could not find “wait”
@aaronjwood

This comment has been minimized.

Show comment
Hide comment
@aaronjwood

aaronjwood May 5, 2017

@alphapapa

type wait
wait is /usr/bin/wait

It seems that fish picks up on shell builtins. If not I would think tons of stuff wouldn't work like cd or true.

aaronjwood commented May 5, 2017

@alphapapa

type wait
wait is /usr/bin/wait

It seems that fish picks up on shell builtins. If not I would think tons of stuff wouldn't work like cd or true.

@aaronjwood

This comment has been minimized.

Show comment
Hide comment
@aaronjwood

aaronjwood May 5, 2017

Actually, my understanding is that wait should be picked up as a builtin. Similar to:

type true
true is a builtin

I don't know why fish is picking wait up as not a builtin...

Without fish:

which true
/usr/bin/true

which wait
/usr/bin/wait

type true
true is a shell builtin

type wait
wait is a shell builtin

With fish:

which true
/usr/bin/true

which wait
/usr/bin/wait

type true
true is a builtin

type wait
wait is /usr/bin/wait

aaronjwood commented May 5, 2017

Actually, my understanding is that wait should be picked up as a builtin. Similar to:

type true
true is a builtin

I don't know why fish is picking wait up as not a builtin...

Without fish:

which true
/usr/bin/true

which wait
/usr/bin/wait

type true
true is a shell builtin

type wait
wait is a shell builtin

With fish:

which true
/usr/bin/true

which wait
/usr/bin/wait

type true
true is a builtin

type wait
wait is /usr/bin/wait
@alphapapa

This comment has been minimized.

Show comment
Hide comment
@alphapapa

alphapapa May 5, 2017

@aaronjwood I'm not sure what you mean. Bash provides wait as a builtin, but Fish doesn't. I don't have /usr/bin/wait on my systems, either Ubuntu or Debian. What system are you using?

alphapapa commented May 5, 2017

@aaronjwood I'm not sure what you mean. Bash provides wait as a builtin, but Fish doesn't. I don't have /usr/bin/wait on my systems, either Ubuntu or Debian. What system are you using?

@aaronjwood

This comment has been minimized.

Show comment
Hide comment
@aaronjwood

aaronjwood May 5, 2017

I was on OSX when I did that. Maybe I misunderstood what fish is supposed to be. I thought it's still bash with some nice enhancements on top. Is fish supposed to be a totally custom shell with its own syntax and features?

My understanding was that it's supposed to still be bash in the end. If the goal of fish is not to enhance bash or be compatible with it then I guess you can ignore what I said.

aaronjwood commented May 5, 2017

I was on OSX when I did that. Maybe I misunderstood what fish is supposed to be. I thought it's still bash with some nice enhancements on top. Is fish supposed to be a totally custom shell with its own syntax and features?

My understanding was that it's supposed to still be bash in the end. If the goal of fish is not to enhance bash or be compatible with it then I guess you can ignore what I said.

@krader1961

This comment has been minimized.

Show comment
Hide comment
@krader1961

krader1961 May 5, 2017

Contributor

@aaronjwood, I'm using macOS and have no idea how /usr/bin/wait could work. It does appear to try to do what is typically associated with this command as shown by this error message:

/usr/bin/wait: line 4: wait: pid 3906 is not a child of this shell

That was from trying to wait for a job I did launch from fish. I don't see how that could possibly work as an external command. It is simply a wrapper around the wait() or wait4() syscall and such can't wait for a process launched by its parent shell. It's like cd in that it has to be a builtin of the shell.

My understanding was that it's supposed to still be bash in the end.

Your understanding is incorrect. Fish deliberately breaks from the POSIX 1003.1 standard that bash is based on. This is something we could possibly document better. Having said that we definitely should have a wait builtin. It's just that no one has been sufficiently motivated to implement it. Patches are welcome 😄

Contributor

krader1961 commented May 5, 2017

@aaronjwood, I'm using macOS and have no idea how /usr/bin/wait could work. It does appear to try to do what is typically associated with this command as shown by this error message:

/usr/bin/wait: line 4: wait: pid 3906 is not a child of this shell

That was from trying to wait for a job I did launch from fish. I don't see how that could possibly work as an external command. It is simply a wrapper around the wait() or wait4() syscall and such can't wait for a process launched by its parent shell. It's like cd in that it has to be a builtin of the shell.

My understanding was that it's supposed to still be bash in the end.

Your understanding is incorrect. Fish deliberately breaks from the POSIX 1003.1 standard that bash is based on. This is something we could possibly document better. Having said that we definitely should have a wait builtin. It's just that no one has been sufficiently motivated to implement it. Patches are welcome 😄

@aaronjwood

This comment has been minimized.

Show comment
Hide comment
@aaronjwood

aaronjwood May 5, 2017

Sure, makes sense. Thanks for clarifying about the standard!

aaronjwood commented May 5, 2017

Sure, makes sense. Thanks for clarifying about the standard!

@Debilski

This comment has been minimized.

Show comment
Hide comment
@Debilski

Debilski May 6, 2017

By the way: the macOS/FreeBSD /usr/bin/wait is nothing more than a shell script calling the builtin in a slightly convoluted way:

#!/bin/sh
builtin `echo ${0##*/} | tr \[:upper:] \[:lower:]` ${1+"$@"}

Debilski commented May 6, 2017

By the way: the macOS/FreeBSD /usr/bin/wait is nothing more than a shell script calling the builtin in a slightly convoluted way:

#!/bin/sh
builtin `echo ${0##*/} | tr \[:upper:] \[:lower:]` ${1+"$@"}
@krader1961

This comment has been minimized.

Show comment
Hide comment
@krader1961

krader1961 May 7, 2017

Contributor

@Debilski, Can you explain how that works? Because an external command cannot interact with its parent shell in the way this one appears to be doing. This appears to rely on the parent shell sourcing that script for the sole purpose of allowing the user to write WAIT %jobspec. That is, converting the wait command to all lower case. Which itself doesn't seem to be useful since the user has presumably already typed wait in order to find and source that script.

Googling did not result in any useful documents. I did find some, such as this one which are totally unhelpful in explaining why an external script named wait can be used to wait for jobs launched by the parent shell to terminate. This script/command appears to depend on some quirk of BSD that allows a program to wait for an arbitrary process to exit -- not just child processes of the current process.

Contributor

krader1961 commented May 7, 2017

@Debilski, Can you explain how that works? Because an external command cannot interact with its parent shell in the way this one appears to be doing. This appears to rely on the parent shell sourcing that script for the sole purpose of allowing the user to write WAIT %jobspec. That is, converting the wait command to all lower case. Which itself doesn't seem to be useful since the user has presumably already typed wait in order to find and source that script.

Googling did not result in any useful documents. I did find some, such as this one which are totally unhelpful in explaining why an external script named wait can be used to wait for jobs launched by the parent shell to terminate. This script/command appears to depend on some quirk of BSD that allows a program to wait for an arbitrary process to exit -- not just child processes of the current process.

@Debilski

This comment has been minimized.

Show comment
Hide comment
@Debilski

Debilski May 7, 2017

@krader1961 The lowercasing would be because of case-insensitive filesystems. WAIT (or CD and a few more commands) will find the correct script there (and then do nothing at all because it obviously doesn’t work).

Here is a little explanation for the rest https://superuser.com/q/69869 . Maybe it makes sense for one of the other commands that are aliased to the very same script.

Debilski commented May 7, 2017

@krader1961 The lowercasing would be because of case-insensitive filesystems. WAIT (or CD and a few more commands) will find the correct script there (and then do nothing at all because it obviously doesn’t work).

Here is a little explanation for the rest https://superuser.com/q/69869 . Maybe it makes sense for one of the other commands that are aliased to the very same script.

@aaronjwood

This comment has been minimized.

Show comment
Hide comment
@aaronjwood

aaronjwood May 8, 2017

@Debilski well I certainly learned something today! Never realized these scripts were there to fulfill POSIX requirements. More good info here that points out a few useful cases such as exit status https://unix.stackexchange.com/a/50060/40967

aaronjwood commented May 8, 2017

@Debilski well I certainly learned something today! Never realized these scripts were there to fulfill POSIX requirements. More good info here that points out a few useful cases such as exit status https://unix.stackexchange.com/a/50060/40967

@pirate

This comment has been minimized.

Show comment
Hide comment
@pirate

pirate May 20, 2017

I've implemented my own subshells, wait, & signals for fish if anyone wants to check them out: await.fish, progress_await.fish, signal.fish, background.fish, fork. They're by no means perfect, I'm no expert on shells in general (I welcome corrections).

These certainly aren't as ideal as builtin solutions, but they maybe be useful for people looking for interim solutions until backgrounding and subshells are supported natively.

pirate commented May 20, 2017

I've implemented my own subshells, wait, & signals for fish if anyone wants to check them out: await.fish, progress_await.fish, signal.fish, background.fish, fork. They're by no means perfect, I'm no expert on shells in general (I welcome corrections).

These certainly aren't as ideal as builtin solutions, but they maybe be useful for people looking for interim solutions until backgrounding and subshells are supported natively.

@arcanis

This comment has been minimized.

Show comment
Hide comment
@arcanis

arcanis Jun 16, 2017

Subshells are useful when one wish to group a command output:

(tput a; printf something; tput b) | cat -e

arcanis commented Jun 16, 2017

Subshells are useful when one wish to group a command output:

(tput a; printf something; tput b) | cat -e
@faho

This comment has been minimized.

Show comment
Hide comment
@faho

faho Jun 16, 2017

Member

@arcanis: That particular case is actually already possible with begin and end - begin; echo banana; echo grapefruit; end | grep banana.

The issue only starts happening when you're expecting certain state to not be carried over, like $PWD.

Member

faho commented Jun 16, 2017

@arcanis: That particular case is actually already possible with begin and end - begin; echo banana; echo grapefruit; end | grep banana.

The issue only starts happening when you're expecting certain state to not be carried over, like $PWD.

@jtgans

This comment has been minimized.

Show comment
Hide comment
@jtgans

jtgans Jan 25, 2018

The problem with using begin; ...; end |grep is the buffering involved. Unfortunately, this means that if I need to construct streams for large amounts of data, the begin and end solution doesn't actually solve the problem.

jtgans commented Jan 25, 2018

The problem with using begin; ...; end |grep is the buffering involved. Unfortunately, this means that if I need to construct streams for large amounts of data, the begin and end solution doesn't actually solve the problem.

@aario

This comment has been minimized.

Show comment
Hide comment
@aario

aario Jul 16, 2018

Simply using back tics ` and having a subshell and then passing the result to the command like running a loop over results of find command:

for file in `find ./ -type f`; ...

Is really awesome. I think the only way to have this feature is to fork fish!

aario commented Jul 16, 2018

Simply using back tics ` and having a subshell and then passing the result to the command like running a loop over results of find command:

for file in `find ./ -type f`; ...

Is really awesome. I think the only way to have this feature is to fork fish!

@faho

This comment has been minimized.

Show comment
Hide comment
@faho

faho Jul 16, 2018

Member

@aario:

running a loop over results of find command is really awesome

It is!

I think the only way to have this feature is to fork fish!

Fortunately, it's not:

for file in (find ./ -type f)

Syntactically, the only difference to POSIX-like shells is that you use (). Semantically, this will split finds output on newlines only instead of spaces, tabs and newlines (or rather the contents of $IFS). That's most likely to be a massive improvement because it won't break on files with spaces in the name (which are quite common) - it'll still break with newlines in the filename, but those are quite uncommon.

This issue is about something slightly different - simply put, it's about allowing some form of block that has a separated environment, including the working directory. What you are talking about is called a "command substitution".


(Technically, it is necessary to "fork" here, because find is an external program, and fish executes that by forking. But that's what any shell does, and the point is that you don't need to jump through more hoops with fish than with e.g. bash)

(Also, even in POSIX-like shells like bash, zsh, ksh etc, I'd recommend using $() instead of backticks. See http://mywiki.wooledge.org/BashFAQ/082 for more information)

Member

faho commented Jul 16, 2018

@aario:

running a loop over results of find command is really awesome

It is!

I think the only way to have this feature is to fork fish!

Fortunately, it's not:

for file in (find ./ -type f)

Syntactically, the only difference to POSIX-like shells is that you use (). Semantically, this will split finds output on newlines only instead of spaces, tabs and newlines (or rather the contents of $IFS). That's most likely to be a massive improvement because it won't break on files with spaces in the name (which are quite common) - it'll still break with newlines in the filename, but those are quite uncommon.

This issue is about something slightly different - simply put, it's about allowing some form of block that has a separated environment, including the working directory. What you are talking about is called a "command substitution".


(Technically, it is necessary to "fork" here, because find is an external program, and fish executes that by forking. But that's what any shell does, and the point is that you don't need to jump through more hoops with fish than with e.g. bash)

(Also, even in POSIX-like shells like bash, zsh, ksh etc, I'd recommend using $() instead of backticks. See http://mywiki.wooledge.org/BashFAQ/082 for more information)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment