Compare command output inside if statement without subshell

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP











up vote
3
down vote

favorite
1












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; fiI 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.







share|improve this question
























    up vote
    3
    down vote

    favorite
    1












    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; fiI 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.







    share|improve this question






















      up vote
      3
      down vote

      favorite
      1









      up vote
      3
      down vote

      favorite
      1






      1





      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; fiI 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.







      share|improve this question












      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; fiI 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.









      share|improve this question











      share|improve this question




      share|improve this question










      asked Mar 15 at 21:27









      G G

      185




      185




















          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





          share|improve this answer






















            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',
            convertImagesToLinks: false,
            noModals: false,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: null,
            bindNavPrevention: true,
            postfix: "",
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            );



            );








             

            draft saved


            draft discarded


















            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






























            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





            share|improve this answer


























              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





              share|improve this answer
























                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





                share|improve this answer














                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






                share|improve this answer














                share|improve this answer



                share|improve this answer








                edited Mar 15 at 22:56

























                answered Mar 15 at 22:00









                Stéphane Chazelas

                280k53515847




                280k53515847






















                     

                    draft saved


                    draft discarded


























                     


                    draft saved


                    draft discarded














                    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













































































                    Popular posts from this blog

                    How to check contact read email or not when send email to Individual?

                    Bahrain

                    Postfix configuration issue with fips on centos 7; mailgun relay