Compare command output inside if statement without subshell
Clash Royale CLAN TAG#URR8PPP
up vote
3
down vote
favorite
According to https://www.gnu.org/software/bash/manual/bashref.html#Command-Grouping
Placing a list of commands between curly braces causes the list to be executed in the current shell context.
But when I try this: if [[ type -t echo; = "builtin" ]]; then echo 1; else echo 0; fi
I get the following error:
-bash: conditional binary operator expected
-bash: syntax error near `type'
That's OK, I get it's not meant to be used like that. Now I know that if I use if [[ $( type -t echo ) = "builtin" ]]; then echo 1; else echo 0; fi
I will theoretically achieve the desired functionality.
I found other questions about preventing the unnecessary use of subshells answered but none of them explained comparing the output of a command without a subshell.
All I really want is to avoid using a subshell where it's not really needed and I would really like it, if possible, to run this check in the current shell context - it really feels like this is how it's supposed to be done.
P.S. I don't mind using variables and manipulation if there's no other way.
bash shell string test subshell
add a comment |Â
up vote
3
down vote
favorite
According to https://www.gnu.org/software/bash/manual/bashref.html#Command-Grouping
Placing a list of commands between curly braces causes the list to be executed in the current shell context.
But when I try this: if [[ type -t echo; = "builtin" ]]; then echo 1; else echo 0; fi
I get the following error:
-bash: conditional binary operator expected
-bash: syntax error near `type'
That's OK, I get it's not meant to be used like that. Now I know that if I use if [[ $( type -t echo ) = "builtin" ]]; then echo 1; else echo 0; fi
I will theoretically achieve the desired functionality.
I found other questions about preventing the unnecessary use of subshells answered but none of them explained comparing the output of a command without a subshell.
All I really want is to avoid using a subshell where it's not really needed and I would really like it, if possible, to run this check in the current shell context - it really feels like this is how it's supposed to be done.
P.S. I don't mind using variables and manipulation if there's no other way.
bash shell string test subshell
add a comment |Â
up vote
3
down vote
favorite
up vote
3
down vote
favorite
According to https://www.gnu.org/software/bash/manual/bashref.html#Command-Grouping
Placing a list of commands between curly braces causes the list to be executed in the current shell context.
But when I try this: if [[ type -t echo; = "builtin" ]]; then echo 1; else echo 0; fi
I get the following error:
-bash: conditional binary operator expected
-bash: syntax error near `type'
That's OK, I get it's not meant to be used like that. Now I know that if I use if [[ $( type -t echo ) = "builtin" ]]; then echo 1; else echo 0; fi
I will theoretically achieve the desired functionality.
I found other questions about preventing the unnecessary use of subshells answered but none of them explained comparing the output of a command without a subshell.
All I really want is to avoid using a subshell where it's not really needed and I would really like it, if possible, to run this check in the current shell context - it really feels like this is how it's supposed to be done.
P.S. I don't mind using variables and manipulation if there's no other way.
bash shell string test subshell
According to https://www.gnu.org/software/bash/manual/bashref.html#Command-Grouping
Placing a list of commands between curly braces causes the list to be executed in the current shell context.
But when I try this: if [[ type -t echo; = "builtin" ]]; then echo 1; else echo 0; fi
I get the following error:
-bash: conditional binary operator expected
-bash: syntax error near `type'
That's OK, I get it's not meant to be used like that. Now I know that if I use if [[ $( type -t echo ) = "builtin" ]]; then echo 1; else echo 0; fi
I will theoretically achieve the desired functionality.
I found other questions about preventing the unnecessary use of subshells answered but none of them explained comparing the output of a command without a subshell.
All I really want is to avoid using a subshell where it's not really needed and I would really like it, if possible, to run this check in the current shell context - it really feels like this is how it's supposed to be done.
P.S. I don't mind using variables and manipulation if there's no other way.
bash shell string test subshell
asked Mar 15 at 21:27
G G
185
185
add a comment |Â
add a comment |Â
1 Answer
1
active
oldest
votes
up vote
4
down vote
accepted
To get the output of a command, you need to read it somehow. type
writes it on its stdout. And we need somehow to get that and pass it to the [
command.
$(...)
uses a pipe for that. But for a pipe, you need a writer and a reader process, so you'll have to fork a process even for running a command that is builtin. You can try reading and writing to a pipe in the same process but that's usually prone to deadlocks as the writing can block if nobody is reading (though you need the pipe buffer to get full for that to happen which is unlikely for type
).
You could do it with the yash
shell that has a raw interface to pipe()
:
type echo >&3
echo 3>&- # close the writing end so the reader can see an eof
IFS= read -r answer <&4
3>>|4
Above, you'd get a deadlock if type
's output was larger than the pipe buffer size (64KiB by default on modern versions of Linux).
With bash
, you could always do:
type -t echo > file
IFS= read -rd '' type < file
if [ "$type" = builtin ]...
But while that avoids the subshell, that means littering the file system with that file
.
Here type
is a builtin. Its output is generated by the shell and it's true it feels a bit silly to have to fork a process to be able to use that output in the shell.
Some shells (ksh93
and fish
) do implement some optimisations there. In their $(type echo)
((type echo)
in fish
), they actually fake the writing of the output and the reading of it. When a builtin's stdout is a command substitution, instead of writing the output, the shell just appends the would-be-output text to the command substitution result and there's no need for a fork.
Actually, fish
's (type echo)
is more like ksh93
's $ type echo;
in that it doesn't even create a subshell environment. With $(...)
, ksh93
emulates a subshell environment so that it appears as though a child process was forked to interpret the code in it and doesn't do that for its $ ...;
variant.
ksh93$ a=1
ksh93$ echo "$(a=2; type echo) $a"
echo is a shell builtin 1
ksh93$ echo "$ a=3; type echo; $a"
echo is a shell builtin 3
fish> set a 1
fish> echo (set a 2; type echo) $a
echo is a builtin 2
You'll find that in some shells that don't do that optimisation, a lot of the builtins can be called in such a way where instead of writing their result, they store it in a variable.
The most obvious ones are the standard read
and getopts
that do that by default (you do IFS= read -r var
instead of var=$(line)
). bash
and zsh
also have printf -v variable format args
. zsh
can do the same for its stat
, strftime
... builtins.
Some shells also make some information already available automatically in some special variables, like ksh
's $SECONDS
and $RANDOM
found in a few other shells (and standard ones like $-
, $#
(equivalent to fish
's (count $argv)
for instance)).
In zsh
, that's generalised to most of the shell's internal information so you almost never need to use command substitution on the output of a builtin. For instance, it's got associative arrays for the list of builtins, keywords, commands...
if (($+builtins[echo])); then
echo echo is a builtin
fi
add a comment |Â
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
4
down vote
accepted
To get the output of a command, you need to read it somehow. type
writes it on its stdout. And we need somehow to get that and pass it to the [
command.
$(...)
uses a pipe for that. But for a pipe, you need a writer and a reader process, so you'll have to fork a process even for running a command that is builtin. You can try reading and writing to a pipe in the same process but that's usually prone to deadlocks as the writing can block if nobody is reading (though you need the pipe buffer to get full for that to happen which is unlikely for type
).
You could do it with the yash
shell that has a raw interface to pipe()
:
type echo >&3
echo 3>&- # close the writing end so the reader can see an eof
IFS= read -r answer <&4
3>>|4
Above, you'd get a deadlock if type
's output was larger than the pipe buffer size (64KiB by default on modern versions of Linux).
With bash
, you could always do:
type -t echo > file
IFS= read -rd '' type < file
if [ "$type" = builtin ]...
But while that avoids the subshell, that means littering the file system with that file
.
Here type
is a builtin. Its output is generated by the shell and it's true it feels a bit silly to have to fork a process to be able to use that output in the shell.
Some shells (ksh93
and fish
) do implement some optimisations there. In their $(type echo)
((type echo)
in fish
), they actually fake the writing of the output and the reading of it. When a builtin's stdout is a command substitution, instead of writing the output, the shell just appends the would-be-output text to the command substitution result and there's no need for a fork.
Actually, fish
's (type echo)
is more like ksh93
's $ type echo;
in that it doesn't even create a subshell environment. With $(...)
, ksh93
emulates a subshell environment so that it appears as though a child process was forked to interpret the code in it and doesn't do that for its $ ...;
variant.
ksh93$ a=1
ksh93$ echo "$(a=2; type echo) $a"
echo is a shell builtin 1
ksh93$ echo "$ a=3; type echo; $a"
echo is a shell builtin 3
fish> set a 1
fish> echo (set a 2; type echo) $a
echo is a builtin 2
You'll find that in some shells that don't do that optimisation, a lot of the builtins can be called in such a way where instead of writing their result, they store it in a variable.
The most obvious ones are the standard read
and getopts
that do that by default (you do IFS= read -r var
instead of var=$(line)
). bash
and zsh
also have printf -v variable format args
. zsh
can do the same for its stat
, strftime
... builtins.
Some shells also make some information already available automatically in some special variables, like ksh
's $SECONDS
and $RANDOM
found in a few other shells (and standard ones like $-
, $#
(equivalent to fish
's (count $argv)
for instance)).
In zsh
, that's generalised to most of the shell's internal information so you almost never need to use command substitution on the output of a builtin. For instance, it's got associative arrays for the list of builtins, keywords, commands...
if (($+builtins[echo])); then
echo echo is a builtin
fi
add a comment |Â
up vote
4
down vote
accepted
To get the output of a command, you need to read it somehow. type
writes it on its stdout. And we need somehow to get that and pass it to the [
command.
$(...)
uses a pipe for that. But for a pipe, you need a writer and a reader process, so you'll have to fork a process even for running a command that is builtin. You can try reading and writing to a pipe in the same process but that's usually prone to deadlocks as the writing can block if nobody is reading (though you need the pipe buffer to get full for that to happen which is unlikely for type
).
You could do it with the yash
shell that has a raw interface to pipe()
:
type echo >&3
echo 3>&- # close the writing end so the reader can see an eof
IFS= read -r answer <&4
3>>|4
Above, you'd get a deadlock if type
's output was larger than the pipe buffer size (64KiB by default on modern versions of Linux).
With bash
, you could always do:
type -t echo > file
IFS= read -rd '' type < file
if [ "$type" = builtin ]...
But while that avoids the subshell, that means littering the file system with that file
.
Here type
is a builtin. Its output is generated by the shell and it's true it feels a bit silly to have to fork a process to be able to use that output in the shell.
Some shells (ksh93
and fish
) do implement some optimisations there. In their $(type echo)
((type echo)
in fish
), they actually fake the writing of the output and the reading of it. When a builtin's stdout is a command substitution, instead of writing the output, the shell just appends the would-be-output text to the command substitution result and there's no need for a fork.
Actually, fish
's (type echo)
is more like ksh93
's $ type echo;
in that it doesn't even create a subshell environment. With $(...)
, ksh93
emulates a subshell environment so that it appears as though a child process was forked to interpret the code in it and doesn't do that for its $ ...;
variant.
ksh93$ a=1
ksh93$ echo "$(a=2; type echo) $a"
echo is a shell builtin 1
ksh93$ echo "$ a=3; type echo; $a"
echo is a shell builtin 3
fish> set a 1
fish> echo (set a 2; type echo) $a
echo is a builtin 2
You'll find that in some shells that don't do that optimisation, a lot of the builtins can be called in such a way where instead of writing their result, they store it in a variable.
The most obvious ones are the standard read
and getopts
that do that by default (you do IFS= read -r var
instead of var=$(line)
). bash
and zsh
also have printf -v variable format args
. zsh
can do the same for its stat
, strftime
... builtins.
Some shells also make some information already available automatically in some special variables, like ksh
's $SECONDS
and $RANDOM
found in a few other shells (and standard ones like $-
, $#
(equivalent to fish
's (count $argv)
for instance)).
In zsh
, that's generalised to most of the shell's internal information so you almost never need to use command substitution on the output of a builtin. For instance, it's got associative arrays for the list of builtins, keywords, commands...
if (($+builtins[echo])); then
echo echo is a builtin
fi
add a comment |Â
up vote
4
down vote
accepted
up vote
4
down vote
accepted
To get the output of a command, you need to read it somehow. type
writes it on its stdout. And we need somehow to get that and pass it to the [
command.
$(...)
uses a pipe for that. But for a pipe, you need a writer and a reader process, so you'll have to fork a process even for running a command that is builtin. You can try reading and writing to a pipe in the same process but that's usually prone to deadlocks as the writing can block if nobody is reading (though you need the pipe buffer to get full for that to happen which is unlikely for type
).
You could do it with the yash
shell that has a raw interface to pipe()
:
type echo >&3
echo 3>&- # close the writing end so the reader can see an eof
IFS= read -r answer <&4
3>>|4
Above, you'd get a deadlock if type
's output was larger than the pipe buffer size (64KiB by default on modern versions of Linux).
With bash
, you could always do:
type -t echo > file
IFS= read -rd '' type < file
if [ "$type" = builtin ]...
But while that avoids the subshell, that means littering the file system with that file
.
Here type
is a builtin. Its output is generated by the shell and it's true it feels a bit silly to have to fork a process to be able to use that output in the shell.
Some shells (ksh93
and fish
) do implement some optimisations there. In their $(type echo)
((type echo)
in fish
), they actually fake the writing of the output and the reading of it. When a builtin's stdout is a command substitution, instead of writing the output, the shell just appends the would-be-output text to the command substitution result and there's no need for a fork.
Actually, fish
's (type echo)
is more like ksh93
's $ type echo;
in that it doesn't even create a subshell environment. With $(...)
, ksh93
emulates a subshell environment so that it appears as though a child process was forked to interpret the code in it and doesn't do that for its $ ...;
variant.
ksh93$ a=1
ksh93$ echo "$(a=2; type echo) $a"
echo is a shell builtin 1
ksh93$ echo "$ a=3; type echo; $a"
echo is a shell builtin 3
fish> set a 1
fish> echo (set a 2; type echo) $a
echo is a builtin 2
You'll find that in some shells that don't do that optimisation, a lot of the builtins can be called in such a way where instead of writing their result, they store it in a variable.
The most obvious ones are the standard read
and getopts
that do that by default (you do IFS= read -r var
instead of var=$(line)
). bash
and zsh
also have printf -v variable format args
. zsh
can do the same for its stat
, strftime
... builtins.
Some shells also make some information already available automatically in some special variables, like ksh
's $SECONDS
and $RANDOM
found in a few other shells (and standard ones like $-
, $#
(equivalent to fish
's (count $argv)
for instance)).
In zsh
, that's generalised to most of the shell's internal information so you almost never need to use command substitution on the output of a builtin. For instance, it's got associative arrays for the list of builtins, keywords, commands...
if (($+builtins[echo])); then
echo echo is a builtin
fi
To get the output of a command, you need to read it somehow. type
writes it on its stdout. And we need somehow to get that and pass it to the [
command.
$(...)
uses a pipe for that. But for a pipe, you need a writer and a reader process, so you'll have to fork a process even for running a command that is builtin. You can try reading and writing to a pipe in the same process but that's usually prone to deadlocks as the writing can block if nobody is reading (though you need the pipe buffer to get full for that to happen which is unlikely for type
).
You could do it with the yash
shell that has a raw interface to pipe()
:
type echo >&3
echo 3>&- # close the writing end so the reader can see an eof
IFS= read -r answer <&4
3>>|4
Above, you'd get a deadlock if type
's output was larger than the pipe buffer size (64KiB by default on modern versions of Linux).
With bash
, you could always do:
type -t echo > file
IFS= read -rd '' type < file
if [ "$type" = builtin ]...
But while that avoids the subshell, that means littering the file system with that file
.
Here type
is a builtin. Its output is generated by the shell and it's true it feels a bit silly to have to fork a process to be able to use that output in the shell.
Some shells (ksh93
and fish
) do implement some optimisations there. In their $(type echo)
((type echo)
in fish
), they actually fake the writing of the output and the reading of it. When a builtin's stdout is a command substitution, instead of writing the output, the shell just appends the would-be-output text to the command substitution result and there's no need for a fork.
Actually, fish
's (type echo)
is more like ksh93
's $ type echo;
in that it doesn't even create a subshell environment. With $(...)
, ksh93
emulates a subshell environment so that it appears as though a child process was forked to interpret the code in it and doesn't do that for its $ ...;
variant.
ksh93$ a=1
ksh93$ echo "$(a=2; type echo) $a"
echo is a shell builtin 1
ksh93$ echo "$ a=3; type echo; $a"
echo is a shell builtin 3
fish> set a 1
fish> echo (set a 2; type echo) $a
echo is a builtin 2
You'll find that in some shells that don't do that optimisation, a lot of the builtins can be called in such a way where instead of writing their result, they store it in a variable.
The most obvious ones are the standard read
and getopts
that do that by default (you do IFS= read -r var
instead of var=$(line)
). bash
and zsh
also have printf -v variable format args
. zsh
can do the same for its stat
, strftime
... builtins.
Some shells also make some information already available automatically in some special variables, like ksh
's $SECONDS
and $RANDOM
found in a few other shells (and standard ones like $-
, $#
(equivalent to fish
's (count $argv)
for instance)).
In zsh
, that's generalised to most of the shell's internal information so you almost never need to use command substitution on the output of a builtin. For instance, it's got associative arrays for the list of builtins, keywords, commands...
if (($+builtins[echo])); then
echo echo is a builtin
fi
edited Mar 15 at 22:56
answered Mar 15 at 22:00
Stéphane Chazelas
280k53515847
280k53515847
add a comment |Â
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f430494%2fcompare-command-output-inside-if-statement-without-subshell%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password