Compare command output inside if statement without 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
add a comment |
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 |
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
bash shell string test subshell
asked Mar 15 '18 at 21:27
G GG G
185
185
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
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 (FreeBSD sh
also does it for single builtin invocations (like type
here)). 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 |
Your Answer
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "106"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
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
Required, but never shown
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
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
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 (FreeBSD sh
also does it for single builtin invocations (like type
here)). 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 |
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 (FreeBSD sh
also does it for single builtin invocations (like type
here)). 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 |
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 (FreeBSD sh
also does it for single builtin invocations (like type
here)). 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 (FreeBSD sh
also does it for single builtin invocations (like type
here)). 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 Feb 19 at 17:00
answered Mar 15 '18 at 22:00
Stéphane ChazelasStéphane Chazelas
309k57582942
309k57582942
add a comment |
add a comment |
Thanks for contributing an answer to Unix & Linux Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
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
Required, but never shown
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
Required, but never shown
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
Required, but never shown
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
Required, but never shown
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
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown