Why do I need to quote variable for if, but not for echo?

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





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;







up vote
19
down vote

favorite
7












I've read that you need double quotes for expanding variables, e.g.



if [ -n "$test" ]; then echo '$test ok'; else echo '$test null'; fi


will work as expected, while



if [ -n $test ]; then echo '$test ok'; else echo '$test null'; fi


will always say $test ok even if $test is null.



but then why don't we need quotes in echo $test?







share|improve this question















  • 2




    If you don't quote a variable to used as an arg to echo, extra spaces and newlines would be stripped.
    – jordanm
    Feb 21 '13 at 16:28
















up vote
19
down vote

favorite
7












I've read that you need double quotes for expanding variables, e.g.



if [ -n "$test" ]; then echo '$test ok'; else echo '$test null'; fi


will work as expected, while



if [ -n $test ]; then echo '$test ok'; else echo '$test null'; fi


will always say $test ok even if $test is null.



but then why don't we need quotes in echo $test?







share|improve this question















  • 2




    If you don't quote a variable to used as an arg to echo, extra spaces and newlines would be stripped.
    – jordanm
    Feb 21 '13 at 16:28












up vote
19
down vote

favorite
7









up vote
19
down vote

favorite
7






7





I've read that you need double quotes for expanding variables, e.g.



if [ -n "$test" ]; then echo '$test ok'; else echo '$test null'; fi


will work as expected, while



if [ -n $test ]; then echo '$test ok'; else echo '$test null'; fi


will always say $test ok even if $test is null.



but then why don't we need quotes in echo $test?







share|improve this question











I've read that you need double quotes for expanding variables, e.g.



if [ -n "$test" ]; then echo '$test ok'; else echo '$test null'; fi


will work as expected, while



if [ -n $test ]; then echo '$test ok'; else echo '$test null'; fi


will always say $test ok even if $test is null.



but then why don't we need quotes in echo $test?









share|improve this question










share|improve this question




share|improve this question









asked Feb 21 '13 at 13:37









CharlesB

207310




207310







  • 2




    If you don't quote a variable to used as an arg to echo, extra spaces and newlines would be stripped.
    – jordanm
    Feb 21 '13 at 16:28












  • 2




    If you don't quote a variable to used as an arg to echo, extra spaces and newlines would be stripped.
    – jordanm
    Feb 21 '13 at 16:28







2




2




If you don't quote a variable to used as an arg to echo, extra spaces and newlines would be stripped.
– jordanm
Feb 21 '13 at 16:28




If you don't quote a variable to used as an arg to echo, extra spaces and newlines would be stripped.
– jordanm
Feb 21 '13 at 16:28










5 Answers
5






active

oldest

votes

















up vote
29
down vote



accepted










You always need quotes around variables in all list contexts, that is everywhere the variable may be expanded to multiple values unless you do want the 3 side effects of leaving a variable unquoted.



list contexts include arguments to simple commands like [ or echo, the for i in <here>, assignments to arrays... There are other contexts where variables also need to be quoted. Best is to always quote variables unless you've got a very good reason not to.



Think of the absence of quotes (in list contexts) as the split+glob operator.



As if echo $test was echo glob(split("$test")).



The shell behaviour is confusing to most people because in most other languages, you put quotes around fixed strings, like puts("foo"), and not around variables (like puts(var)) while in shell it's the other way round: everything is string in shell, so putting quotes around everything would be cumbersome, you echo test, you don't need to "echo" "test". In shell, quotes are used for something else: prevent some special meaning of some characters and/or affect the behaviour of some expansions.



In [ -n $test ] or echo $test, the shell will split $test (on blanks by default), and then perform filename generation (expand all the *, '?'... patterns to the list of matching files), and then pass that list of arguments to the [ or echo commands.



Again, think of it as "[" "-n" glob(split("$test")) "]". If $test is empty or contains only blanks (spc, tab, nl), then the split+glob operator will return an empty list, so the [ -n $test ] will be "[" "-n" "]", which is a test to check wheter "-n" is the empty string or not. But imagine what would have happened if $test was "*" or "= foo"...



In [ -n "$test" ], [ is passed the four arguments "[", "-n", "" and "]" (without the quotes), which is what we want.



Whether it's echo or [ makes no difference, it's just that echo outputs the same thing whether it's passed an empty argument or no argument at all.



See also this answer to a similar question for more details on the [ command and the [[...]] construct.






share|improve this answer






























    up vote
    6
    down vote













    @h3rrmiller's answer is good for explaining why you need the quotes for the if (or rather, [/test), but I would actually posit that your question is incorrect.



    Try the following commands, and you will see what I mean.



    export testvar="123 456"
    echo $testvar
    echo "$testvar"


    Without the quotes, the variable substitution causes the second command to expand to:



    echo 123 456


    and the multiple spaces are collapsed to a single one:



    echo 123 456


    With the quotes, the spaces are preserved.



    This happens because when you quote a parameter (whether that parameter is passed to echo, test or some other command), the value of that parameter is sent as one value to the command. If you don't quote it, the shell does its normal magic of looking for whitespace to determine where each parameter starts and ends.



    This can also be illustrated by the following (very very simple) C program. Try the following on the command line (you may want to do it in an empty directory so as to not risking to overwrite something).



    cat <<EOF >paramtest.c
    #include <stdio.h>
    int main(int argc, char **argv)
    int nparams = argc-1; /* because 1 parameter means only the executable's name */
    printf("%d parameters receivedn", nparams);
    return nparams;

    EOF
    cc -o paramtest paramtest.c


    and then...



    ./paramtest 123 456
    ./paramtest "123 456"
    ./paramtest 123 456
    ./paramtest "123 456"


    After running paramtest, $? will hold the number of parameters it was passed (and that number will be printed).






    share|improve this answer






























      up vote
      2
      down vote













      This is all about how the shell interprets the line before a program is executed.



      If the line reads echo I am $USER, the shell expands it to echo I am blrfl and echo has no clue whether the origin of the text is a literal or a variable expansion. Similarly, if a line reads echo I am $UNDEFINED, the shell will expand $UNDEFINED into nothing and echo's arguments will be I am, and that's the end of it. Since echo works just fine with no arguments, echo $UNDEFINED is completely valid.



      Your issue with if isn't really with if, because if just runs whatever program and arguments follow it and executes the then part if the program exits 0 (or the else part if there is one and the program exits non-0):



      if /bin/true ; then echo True dat. ; fi
      if fgrep -q blrfl /etc/passwd ; then echo Blrfl has an account. ; fi


      When you use if [ ... ] to do a comparison, you're not using primitives built into the shell. You're actually instructing the shell to run a program called [ which is a very slight superset of test(1) that requires its last argument be ]. Both programs exit 0 if the test condition came out true and 1 if it didn't.



      The reason some tests break when a variable is undefined is because test doesn't see that you're using a variable. Ergo, [ $UNDEFINED -eq 2 ] breaks because by the time the shell is done with it, all test sees for arguments are -eq 2 ], which isn't a valid test. If you did it with something defined, such as [ $DEFINED -ne 0 ], that would work because the shell would expand it into a valid test (e.g., 0 -ne 0).



      There's a semantic difference between foo $UNDEFINED bar, which expands to two arguments (foo and bar) because $UNDEFINED lived up to its name. Compare this with foo "$UNDEFINED" bar, which expands to three arguments (foo, an empty string and `bar). Quotes force the shell to interpret them as an argument whether there's anything between them or not.






      share|improve this answer




























        up vote
        0
        down vote













        Without quotes $test could expand to be more than one word so it needs to be quoted in order to not break the syntax since each switch inside the [ command is expecting one argument which is what the quotes do (makes whatever $test expands to into one argument)



        The reason you don't need quotes to expand a variable with echo is because it isn't expecting one argument. It will simply print what you tell it to. So even if $test expands to 100 words echo will still print it.



        Take a look at Bash Pitfalls






        share|improve this answer























        • yes but why don't we need it for echo?
          – CharlesB
          Feb 21 '13 at 13:51










        • @CharlesB You do need the quotes for echo. What makes you think otherwise?
          – Gilles
          Feb 21 '13 at 21:37










        • I don't need them, I can echo $test and it works (it outputs the value of $test)
          – CharlesB
          Feb 21 '13 at 22:05






        • 1




          @CharlesB It only outputs the value of $test if that does not contain multiple spaces anywhere. Try the program in my answer for an illustration of the reason.
          – Michael Kjörling
          Feb 22 '13 at 9:17


















        up vote
        0
        down vote













        Empty parameters are removed if not quoted:



        start cmd:> strace -e trace=execve echo foo $bar baz
        execve("/usr/bin/echo", ["echo", "foo", "baz"], [/* 100 vars */]) = 0

        start cmd:> strace -e trace=execve echo foo "$bar" baz
        execve("/usr/bin/echo", ["echo", "foo", "", "baz"], [/* 100 vars */]) = 0


        The called command doesn't see that there was an empty parameter on the shell command line. It seems that [ is defined to return 0 for -n with nothing following. Whyever.



        Quoting does make a difference for echo, too, in several cases:



        var='*'
        echo $var
        echo "$var"

        var="foo bar"
        echo $var
        echo "$var"





        share|improve this answer

















        • 2




          It's not echo, it's the shell. You'd see the same behavior with ls. Try touch '*' some time if you feel adventurous. :)
          – Michael Kjörling
          Feb 21 '13 at 14:18










        • That's just wording as there is no difference to the 'if [ ... ]` case. [ is not a special shell command. That's different from [[ (in bash) where quoting is not necessary.
          – Hauke Laging
          Feb 21 '13 at 15:06










        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%2f65624%2fwhy-do-i-need-to-quote-variable-for-if-but-not-for-echo%23new-answer', 'question_page');

        );

        Post as a guest






























        5 Answers
        5






        active

        oldest

        votes








        5 Answers
        5






        active

        oldest

        votes









        active

        oldest

        votes






        active

        oldest

        votes








        up vote
        29
        down vote



        accepted










        You always need quotes around variables in all list contexts, that is everywhere the variable may be expanded to multiple values unless you do want the 3 side effects of leaving a variable unquoted.



        list contexts include arguments to simple commands like [ or echo, the for i in <here>, assignments to arrays... There are other contexts where variables also need to be quoted. Best is to always quote variables unless you've got a very good reason not to.



        Think of the absence of quotes (in list contexts) as the split+glob operator.



        As if echo $test was echo glob(split("$test")).



        The shell behaviour is confusing to most people because in most other languages, you put quotes around fixed strings, like puts("foo"), and not around variables (like puts(var)) while in shell it's the other way round: everything is string in shell, so putting quotes around everything would be cumbersome, you echo test, you don't need to "echo" "test". In shell, quotes are used for something else: prevent some special meaning of some characters and/or affect the behaviour of some expansions.



        In [ -n $test ] or echo $test, the shell will split $test (on blanks by default), and then perform filename generation (expand all the *, '?'... patterns to the list of matching files), and then pass that list of arguments to the [ or echo commands.



        Again, think of it as "[" "-n" glob(split("$test")) "]". If $test is empty or contains only blanks (spc, tab, nl), then the split+glob operator will return an empty list, so the [ -n $test ] will be "[" "-n" "]", which is a test to check wheter "-n" is the empty string or not. But imagine what would have happened if $test was "*" or "= foo"...



        In [ -n "$test" ], [ is passed the four arguments "[", "-n", "" and "]" (without the quotes), which is what we want.



        Whether it's echo or [ makes no difference, it's just that echo outputs the same thing whether it's passed an empty argument or no argument at all.



        See also this answer to a similar question for more details on the [ command and the [[...]] construct.






        share|improve this answer



























          up vote
          29
          down vote



          accepted










          You always need quotes around variables in all list contexts, that is everywhere the variable may be expanded to multiple values unless you do want the 3 side effects of leaving a variable unquoted.



          list contexts include arguments to simple commands like [ or echo, the for i in <here>, assignments to arrays... There are other contexts where variables also need to be quoted. Best is to always quote variables unless you've got a very good reason not to.



          Think of the absence of quotes (in list contexts) as the split+glob operator.



          As if echo $test was echo glob(split("$test")).



          The shell behaviour is confusing to most people because in most other languages, you put quotes around fixed strings, like puts("foo"), and not around variables (like puts(var)) while in shell it's the other way round: everything is string in shell, so putting quotes around everything would be cumbersome, you echo test, you don't need to "echo" "test". In shell, quotes are used for something else: prevent some special meaning of some characters and/or affect the behaviour of some expansions.



          In [ -n $test ] or echo $test, the shell will split $test (on blanks by default), and then perform filename generation (expand all the *, '?'... patterns to the list of matching files), and then pass that list of arguments to the [ or echo commands.



          Again, think of it as "[" "-n" glob(split("$test")) "]". If $test is empty or contains only blanks (spc, tab, nl), then the split+glob operator will return an empty list, so the [ -n $test ] will be "[" "-n" "]", which is a test to check wheter "-n" is the empty string or not. But imagine what would have happened if $test was "*" or "= foo"...



          In [ -n "$test" ], [ is passed the four arguments "[", "-n", "" and "]" (without the quotes), which is what we want.



          Whether it's echo or [ makes no difference, it's just that echo outputs the same thing whether it's passed an empty argument or no argument at all.



          See also this answer to a similar question for more details on the [ command and the [[...]] construct.






          share|improve this answer

























            up vote
            29
            down vote



            accepted







            up vote
            29
            down vote



            accepted






            You always need quotes around variables in all list contexts, that is everywhere the variable may be expanded to multiple values unless you do want the 3 side effects of leaving a variable unquoted.



            list contexts include arguments to simple commands like [ or echo, the for i in <here>, assignments to arrays... There are other contexts where variables also need to be quoted. Best is to always quote variables unless you've got a very good reason not to.



            Think of the absence of quotes (in list contexts) as the split+glob operator.



            As if echo $test was echo glob(split("$test")).



            The shell behaviour is confusing to most people because in most other languages, you put quotes around fixed strings, like puts("foo"), and not around variables (like puts(var)) while in shell it's the other way round: everything is string in shell, so putting quotes around everything would be cumbersome, you echo test, you don't need to "echo" "test". In shell, quotes are used for something else: prevent some special meaning of some characters and/or affect the behaviour of some expansions.



            In [ -n $test ] or echo $test, the shell will split $test (on blanks by default), and then perform filename generation (expand all the *, '?'... patterns to the list of matching files), and then pass that list of arguments to the [ or echo commands.



            Again, think of it as "[" "-n" glob(split("$test")) "]". If $test is empty or contains only blanks (spc, tab, nl), then the split+glob operator will return an empty list, so the [ -n $test ] will be "[" "-n" "]", which is a test to check wheter "-n" is the empty string or not. But imagine what would have happened if $test was "*" or "= foo"...



            In [ -n "$test" ], [ is passed the four arguments "[", "-n", "" and "]" (without the quotes), which is what we want.



            Whether it's echo or [ makes no difference, it's just that echo outputs the same thing whether it's passed an empty argument or no argument at all.



            See also this answer to a similar question for more details on the [ command and the [[...]] construct.






            share|improve this answer















            You always need quotes around variables in all list contexts, that is everywhere the variable may be expanded to multiple values unless you do want the 3 side effects of leaving a variable unquoted.



            list contexts include arguments to simple commands like [ or echo, the for i in <here>, assignments to arrays... There are other contexts where variables also need to be quoted. Best is to always quote variables unless you've got a very good reason not to.



            Think of the absence of quotes (in list contexts) as the split+glob operator.



            As if echo $test was echo glob(split("$test")).



            The shell behaviour is confusing to most people because in most other languages, you put quotes around fixed strings, like puts("foo"), and not around variables (like puts(var)) while in shell it's the other way round: everything is string in shell, so putting quotes around everything would be cumbersome, you echo test, you don't need to "echo" "test". In shell, quotes are used for something else: prevent some special meaning of some characters and/or affect the behaviour of some expansions.



            In [ -n $test ] or echo $test, the shell will split $test (on blanks by default), and then perform filename generation (expand all the *, '?'... patterns to the list of matching files), and then pass that list of arguments to the [ or echo commands.



            Again, think of it as "[" "-n" glob(split("$test")) "]". If $test is empty or contains only blanks (spc, tab, nl), then the split+glob operator will return an empty list, so the [ -n $test ] will be "[" "-n" "]", which is a test to check wheter "-n" is the empty string or not. But imagine what would have happened if $test was "*" or "= foo"...



            In [ -n "$test" ], [ is passed the four arguments "[", "-n", "" and "]" (without the quotes), which is what we want.



            Whether it's echo or [ makes no difference, it's just that echo outputs the same thing whether it's passed an empty argument or no argument at all.



            See also this answer to a similar question for more details on the [ command and the [[...]] construct.







            share|improve this answer















            share|improve this answer



            share|improve this answer








            edited Apr 13 '17 at 12:36









            Community♦

            1




            1











            answered Feb 21 '13 at 14:00









            Stéphane Chazelas

            278k52512843




            278k52512843






















                up vote
                6
                down vote













                @h3rrmiller's answer is good for explaining why you need the quotes for the if (or rather, [/test), but I would actually posit that your question is incorrect.



                Try the following commands, and you will see what I mean.



                export testvar="123 456"
                echo $testvar
                echo "$testvar"


                Without the quotes, the variable substitution causes the second command to expand to:



                echo 123 456


                and the multiple spaces are collapsed to a single one:



                echo 123 456


                With the quotes, the spaces are preserved.



                This happens because when you quote a parameter (whether that parameter is passed to echo, test or some other command), the value of that parameter is sent as one value to the command. If you don't quote it, the shell does its normal magic of looking for whitespace to determine where each parameter starts and ends.



                This can also be illustrated by the following (very very simple) C program. Try the following on the command line (you may want to do it in an empty directory so as to not risking to overwrite something).



                cat <<EOF >paramtest.c
                #include <stdio.h>
                int main(int argc, char **argv)
                int nparams = argc-1; /* because 1 parameter means only the executable's name */
                printf("%d parameters receivedn", nparams);
                return nparams;

                EOF
                cc -o paramtest paramtest.c


                and then...



                ./paramtest 123 456
                ./paramtest "123 456"
                ./paramtest 123 456
                ./paramtest "123 456"


                After running paramtest, $? will hold the number of parameters it was passed (and that number will be printed).






                share|improve this answer



























                  up vote
                  6
                  down vote













                  @h3rrmiller's answer is good for explaining why you need the quotes for the if (or rather, [/test), but I would actually posit that your question is incorrect.



                  Try the following commands, and you will see what I mean.



                  export testvar="123 456"
                  echo $testvar
                  echo "$testvar"


                  Without the quotes, the variable substitution causes the second command to expand to:



                  echo 123 456


                  and the multiple spaces are collapsed to a single one:



                  echo 123 456


                  With the quotes, the spaces are preserved.



                  This happens because when you quote a parameter (whether that parameter is passed to echo, test or some other command), the value of that parameter is sent as one value to the command. If you don't quote it, the shell does its normal magic of looking for whitespace to determine where each parameter starts and ends.



                  This can also be illustrated by the following (very very simple) C program. Try the following on the command line (you may want to do it in an empty directory so as to not risking to overwrite something).



                  cat <<EOF >paramtest.c
                  #include <stdio.h>
                  int main(int argc, char **argv)
                  int nparams = argc-1; /* because 1 parameter means only the executable's name */
                  printf("%d parameters receivedn", nparams);
                  return nparams;

                  EOF
                  cc -o paramtest paramtest.c


                  and then...



                  ./paramtest 123 456
                  ./paramtest "123 456"
                  ./paramtest 123 456
                  ./paramtest "123 456"


                  After running paramtest, $? will hold the number of parameters it was passed (and that number will be printed).






                  share|improve this answer

























                    up vote
                    6
                    down vote










                    up vote
                    6
                    down vote









                    @h3rrmiller's answer is good for explaining why you need the quotes for the if (or rather, [/test), but I would actually posit that your question is incorrect.



                    Try the following commands, and you will see what I mean.



                    export testvar="123 456"
                    echo $testvar
                    echo "$testvar"


                    Without the quotes, the variable substitution causes the second command to expand to:



                    echo 123 456


                    and the multiple spaces are collapsed to a single one:



                    echo 123 456


                    With the quotes, the spaces are preserved.



                    This happens because when you quote a parameter (whether that parameter is passed to echo, test or some other command), the value of that parameter is sent as one value to the command. If you don't quote it, the shell does its normal magic of looking for whitespace to determine where each parameter starts and ends.



                    This can also be illustrated by the following (very very simple) C program. Try the following on the command line (you may want to do it in an empty directory so as to not risking to overwrite something).



                    cat <<EOF >paramtest.c
                    #include <stdio.h>
                    int main(int argc, char **argv)
                    int nparams = argc-1; /* because 1 parameter means only the executable's name */
                    printf("%d parameters receivedn", nparams);
                    return nparams;

                    EOF
                    cc -o paramtest paramtest.c


                    and then...



                    ./paramtest 123 456
                    ./paramtest "123 456"
                    ./paramtest 123 456
                    ./paramtest "123 456"


                    After running paramtest, $? will hold the number of parameters it was passed (and that number will be printed).






                    share|improve this answer















                    @h3rrmiller's answer is good for explaining why you need the quotes for the if (or rather, [/test), but I would actually posit that your question is incorrect.



                    Try the following commands, and you will see what I mean.



                    export testvar="123 456"
                    echo $testvar
                    echo "$testvar"


                    Without the quotes, the variable substitution causes the second command to expand to:



                    echo 123 456


                    and the multiple spaces are collapsed to a single one:



                    echo 123 456


                    With the quotes, the spaces are preserved.



                    This happens because when you quote a parameter (whether that parameter is passed to echo, test or some other command), the value of that parameter is sent as one value to the command. If you don't quote it, the shell does its normal magic of looking for whitespace to determine where each parameter starts and ends.



                    This can also be illustrated by the following (very very simple) C program. Try the following on the command line (you may want to do it in an empty directory so as to not risking to overwrite something).



                    cat <<EOF >paramtest.c
                    #include <stdio.h>
                    int main(int argc, char **argv)
                    int nparams = argc-1; /* because 1 parameter means only the executable's name */
                    printf("%d parameters receivedn", nparams);
                    return nparams;

                    EOF
                    cc -o paramtest paramtest.c


                    and then...



                    ./paramtest 123 456
                    ./paramtest "123 456"
                    ./paramtest 123 456
                    ./paramtest "123 456"


                    After running paramtest, $? will hold the number of parameters it was passed (and that number will be printed).







                    share|improve this answer















                    share|improve this answer



                    share|improve this answer








                    edited Apr 13 '17 at 12:37









                    Community♦

                    1




                    1











                    answered Feb 21 '13 at 14:05









                    Michael Kjörling

                    15.6k74595




                    15.6k74595




















                        up vote
                        2
                        down vote













                        This is all about how the shell interprets the line before a program is executed.



                        If the line reads echo I am $USER, the shell expands it to echo I am blrfl and echo has no clue whether the origin of the text is a literal or a variable expansion. Similarly, if a line reads echo I am $UNDEFINED, the shell will expand $UNDEFINED into nothing and echo's arguments will be I am, and that's the end of it. Since echo works just fine with no arguments, echo $UNDEFINED is completely valid.



                        Your issue with if isn't really with if, because if just runs whatever program and arguments follow it and executes the then part if the program exits 0 (or the else part if there is one and the program exits non-0):



                        if /bin/true ; then echo True dat. ; fi
                        if fgrep -q blrfl /etc/passwd ; then echo Blrfl has an account. ; fi


                        When you use if [ ... ] to do a comparison, you're not using primitives built into the shell. You're actually instructing the shell to run a program called [ which is a very slight superset of test(1) that requires its last argument be ]. Both programs exit 0 if the test condition came out true and 1 if it didn't.



                        The reason some tests break when a variable is undefined is because test doesn't see that you're using a variable. Ergo, [ $UNDEFINED -eq 2 ] breaks because by the time the shell is done with it, all test sees for arguments are -eq 2 ], which isn't a valid test. If you did it with something defined, such as [ $DEFINED -ne 0 ], that would work because the shell would expand it into a valid test (e.g., 0 -ne 0).



                        There's a semantic difference between foo $UNDEFINED bar, which expands to two arguments (foo and bar) because $UNDEFINED lived up to its name. Compare this with foo "$UNDEFINED" bar, which expands to three arguments (foo, an empty string and `bar). Quotes force the shell to interpret them as an argument whether there's anything between them or not.






                        share|improve this answer

























                          up vote
                          2
                          down vote













                          This is all about how the shell interprets the line before a program is executed.



                          If the line reads echo I am $USER, the shell expands it to echo I am blrfl and echo has no clue whether the origin of the text is a literal or a variable expansion. Similarly, if a line reads echo I am $UNDEFINED, the shell will expand $UNDEFINED into nothing and echo's arguments will be I am, and that's the end of it. Since echo works just fine with no arguments, echo $UNDEFINED is completely valid.



                          Your issue with if isn't really with if, because if just runs whatever program and arguments follow it and executes the then part if the program exits 0 (or the else part if there is one and the program exits non-0):



                          if /bin/true ; then echo True dat. ; fi
                          if fgrep -q blrfl /etc/passwd ; then echo Blrfl has an account. ; fi


                          When you use if [ ... ] to do a comparison, you're not using primitives built into the shell. You're actually instructing the shell to run a program called [ which is a very slight superset of test(1) that requires its last argument be ]. Both programs exit 0 if the test condition came out true and 1 if it didn't.



                          The reason some tests break when a variable is undefined is because test doesn't see that you're using a variable. Ergo, [ $UNDEFINED -eq 2 ] breaks because by the time the shell is done with it, all test sees for arguments are -eq 2 ], which isn't a valid test. If you did it with something defined, such as [ $DEFINED -ne 0 ], that would work because the shell would expand it into a valid test (e.g., 0 -ne 0).



                          There's a semantic difference between foo $UNDEFINED bar, which expands to two arguments (foo and bar) because $UNDEFINED lived up to its name. Compare this with foo "$UNDEFINED" bar, which expands to three arguments (foo, an empty string and `bar). Quotes force the shell to interpret them as an argument whether there's anything between them or not.






                          share|improve this answer























                            up vote
                            2
                            down vote










                            up vote
                            2
                            down vote









                            This is all about how the shell interprets the line before a program is executed.



                            If the line reads echo I am $USER, the shell expands it to echo I am blrfl and echo has no clue whether the origin of the text is a literal or a variable expansion. Similarly, if a line reads echo I am $UNDEFINED, the shell will expand $UNDEFINED into nothing and echo's arguments will be I am, and that's the end of it. Since echo works just fine with no arguments, echo $UNDEFINED is completely valid.



                            Your issue with if isn't really with if, because if just runs whatever program and arguments follow it and executes the then part if the program exits 0 (or the else part if there is one and the program exits non-0):



                            if /bin/true ; then echo True dat. ; fi
                            if fgrep -q blrfl /etc/passwd ; then echo Blrfl has an account. ; fi


                            When you use if [ ... ] to do a comparison, you're not using primitives built into the shell. You're actually instructing the shell to run a program called [ which is a very slight superset of test(1) that requires its last argument be ]. Both programs exit 0 if the test condition came out true and 1 if it didn't.



                            The reason some tests break when a variable is undefined is because test doesn't see that you're using a variable. Ergo, [ $UNDEFINED -eq 2 ] breaks because by the time the shell is done with it, all test sees for arguments are -eq 2 ], which isn't a valid test. If you did it with something defined, such as [ $DEFINED -ne 0 ], that would work because the shell would expand it into a valid test (e.g., 0 -ne 0).



                            There's a semantic difference between foo $UNDEFINED bar, which expands to two arguments (foo and bar) because $UNDEFINED lived up to its name. Compare this with foo "$UNDEFINED" bar, which expands to three arguments (foo, an empty string and `bar). Quotes force the shell to interpret them as an argument whether there's anything between them or not.






                            share|improve this answer













                            This is all about how the shell interprets the line before a program is executed.



                            If the line reads echo I am $USER, the shell expands it to echo I am blrfl and echo has no clue whether the origin of the text is a literal or a variable expansion. Similarly, if a line reads echo I am $UNDEFINED, the shell will expand $UNDEFINED into nothing and echo's arguments will be I am, and that's the end of it. Since echo works just fine with no arguments, echo $UNDEFINED is completely valid.



                            Your issue with if isn't really with if, because if just runs whatever program and arguments follow it and executes the then part if the program exits 0 (or the else part if there is one and the program exits non-0):



                            if /bin/true ; then echo True dat. ; fi
                            if fgrep -q blrfl /etc/passwd ; then echo Blrfl has an account. ; fi


                            When you use if [ ... ] to do a comparison, you're not using primitives built into the shell. You're actually instructing the shell to run a program called [ which is a very slight superset of test(1) that requires its last argument be ]. Both programs exit 0 if the test condition came out true and 1 if it didn't.



                            The reason some tests break when a variable is undefined is because test doesn't see that you're using a variable. Ergo, [ $UNDEFINED -eq 2 ] breaks because by the time the shell is done with it, all test sees for arguments are -eq 2 ], which isn't a valid test. If you did it with something defined, such as [ $DEFINED -ne 0 ], that would work because the shell would expand it into a valid test (e.g., 0 -ne 0).



                            There's a semantic difference between foo $UNDEFINED bar, which expands to two arguments (foo and bar) because $UNDEFINED lived up to its name. Compare this with foo "$UNDEFINED" bar, which expands to three arguments (foo, an empty string and `bar). Quotes force the shell to interpret them as an argument whether there's anything between them or not.







                            share|improve this answer













                            share|improve this answer



                            share|improve this answer











                            answered Feb 21 '13 at 19:17









                            Blrfl

                            30414




                            30414




















                                up vote
                                0
                                down vote













                                Without quotes $test could expand to be more than one word so it needs to be quoted in order to not break the syntax since each switch inside the [ command is expecting one argument which is what the quotes do (makes whatever $test expands to into one argument)



                                The reason you don't need quotes to expand a variable with echo is because it isn't expecting one argument. It will simply print what you tell it to. So even if $test expands to 100 words echo will still print it.



                                Take a look at Bash Pitfalls






                                share|improve this answer























                                • yes but why don't we need it for echo?
                                  – CharlesB
                                  Feb 21 '13 at 13:51










                                • @CharlesB You do need the quotes for echo. What makes you think otherwise?
                                  – Gilles
                                  Feb 21 '13 at 21:37










                                • I don't need them, I can echo $test and it works (it outputs the value of $test)
                                  – CharlesB
                                  Feb 21 '13 at 22:05






                                • 1




                                  @CharlesB It only outputs the value of $test if that does not contain multiple spaces anywhere. Try the program in my answer for an illustration of the reason.
                                  – Michael Kjörling
                                  Feb 22 '13 at 9:17















                                up vote
                                0
                                down vote













                                Without quotes $test could expand to be more than one word so it needs to be quoted in order to not break the syntax since each switch inside the [ command is expecting one argument which is what the quotes do (makes whatever $test expands to into one argument)



                                The reason you don't need quotes to expand a variable with echo is because it isn't expecting one argument. It will simply print what you tell it to. So even if $test expands to 100 words echo will still print it.



                                Take a look at Bash Pitfalls






                                share|improve this answer























                                • yes but why don't we need it for echo?
                                  – CharlesB
                                  Feb 21 '13 at 13:51










                                • @CharlesB You do need the quotes for echo. What makes you think otherwise?
                                  – Gilles
                                  Feb 21 '13 at 21:37










                                • I don't need them, I can echo $test and it works (it outputs the value of $test)
                                  – CharlesB
                                  Feb 21 '13 at 22:05






                                • 1




                                  @CharlesB It only outputs the value of $test if that does not contain multiple spaces anywhere. Try the program in my answer for an illustration of the reason.
                                  – Michael Kjörling
                                  Feb 22 '13 at 9:17













                                up vote
                                0
                                down vote










                                up vote
                                0
                                down vote









                                Without quotes $test could expand to be more than one word so it needs to be quoted in order to not break the syntax since each switch inside the [ command is expecting one argument which is what the quotes do (makes whatever $test expands to into one argument)



                                The reason you don't need quotes to expand a variable with echo is because it isn't expecting one argument. It will simply print what you tell it to. So even if $test expands to 100 words echo will still print it.



                                Take a look at Bash Pitfalls






                                share|improve this answer















                                Without quotes $test could expand to be more than one word so it needs to be quoted in order to not break the syntax since each switch inside the [ command is expecting one argument which is what the quotes do (makes whatever $test expands to into one argument)



                                The reason you don't need quotes to expand a variable with echo is because it isn't expecting one argument. It will simply print what you tell it to. So even if $test expands to 100 words echo will still print it.



                                Take a look at Bash Pitfalls







                                share|improve this answer















                                share|improve this answer



                                share|improve this answer








                                edited Feb 21 '13 at 13:54


























                                answered Feb 21 '13 at 13:48









                                h3rrmiller

                                8,63942138




                                8,63942138











                                • yes but why don't we need it for echo?
                                  – CharlesB
                                  Feb 21 '13 at 13:51










                                • @CharlesB You do need the quotes for echo. What makes you think otherwise?
                                  – Gilles
                                  Feb 21 '13 at 21:37










                                • I don't need them, I can echo $test and it works (it outputs the value of $test)
                                  – CharlesB
                                  Feb 21 '13 at 22:05






                                • 1




                                  @CharlesB It only outputs the value of $test if that does not contain multiple spaces anywhere. Try the program in my answer for an illustration of the reason.
                                  – Michael Kjörling
                                  Feb 22 '13 at 9:17

















                                • yes but why don't we need it for echo?
                                  – CharlesB
                                  Feb 21 '13 at 13:51










                                • @CharlesB You do need the quotes for echo. What makes you think otherwise?
                                  – Gilles
                                  Feb 21 '13 at 21:37










                                • I don't need them, I can echo $test and it works (it outputs the value of $test)
                                  – CharlesB
                                  Feb 21 '13 at 22:05






                                • 1




                                  @CharlesB It only outputs the value of $test if that does not contain multiple spaces anywhere. Try the program in my answer for an illustration of the reason.
                                  – Michael Kjörling
                                  Feb 22 '13 at 9:17
















                                yes but why don't we need it for echo?
                                – CharlesB
                                Feb 21 '13 at 13:51




                                yes but why don't we need it for echo?
                                – CharlesB
                                Feb 21 '13 at 13:51












                                @CharlesB You do need the quotes for echo. What makes you think otherwise?
                                – Gilles
                                Feb 21 '13 at 21:37




                                @CharlesB You do need the quotes for echo. What makes you think otherwise?
                                – Gilles
                                Feb 21 '13 at 21:37












                                I don't need them, I can echo $test and it works (it outputs the value of $test)
                                – CharlesB
                                Feb 21 '13 at 22:05




                                I don't need them, I can echo $test and it works (it outputs the value of $test)
                                – CharlesB
                                Feb 21 '13 at 22:05




                                1




                                1




                                @CharlesB It only outputs the value of $test if that does not contain multiple spaces anywhere. Try the program in my answer for an illustration of the reason.
                                – Michael Kjörling
                                Feb 22 '13 at 9:17





                                @CharlesB It only outputs the value of $test if that does not contain multiple spaces anywhere. Try the program in my answer for an illustration of the reason.
                                – Michael Kjörling
                                Feb 22 '13 at 9:17











                                up vote
                                0
                                down vote













                                Empty parameters are removed if not quoted:



                                start cmd:> strace -e trace=execve echo foo $bar baz
                                execve("/usr/bin/echo", ["echo", "foo", "baz"], [/* 100 vars */]) = 0

                                start cmd:> strace -e trace=execve echo foo "$bar" baz
                                execve("/usr/bin/echo", ["echo", "foo", "", "baz"], [/* 100 vars */]) = 0


                                The called command doesn't see that there was an empty parameter on the shell command line. It seems that [ is defined to return 0 for -n with nothing following. Whyever.



                                Quoting does make a difference for echo, too, in several cases:



                                var='*'
                                echo $var
                                echo "$var"

                                var="foo bar"
                                echo $var
                                echo "$var"





                                share|improve this answer

















                                • 2




                                  It's not echo, it's the shell. You'd see the same behavior with ls. Try touch '*' some time if you feel adventurous. :)
                                  – Michael Kjörling
                                  Feb 21 '13 at 14:18










                                • That's just wording as there is no difference to the 'if [ ... ]` case. [ is not a special shell command. That's different from [[ (in bash) where quoting is not necessary.
                                  – Hauke Laging
                                  Feb 21 '13 at 15:06














                                up vote
                                0
                                down vote













                                Empty parameters are removed if not quoted:



                                start cmd:> strace -e trace=execve echo foo $bar baz
                                execve("/usr/bin/echo", ["echo", "foo", "baz"], [/* 100 vars */]) = 0

                                start cmd:> strace -e trace=execve echo foo "$bar" baz
                                execve("/usr/bin/echo", ["echo", "foo", "", "baz"], [/* 100 vars */]) = 0


                                The called command doesn't see that there was an empty parameter on the shell command line. It seems that [ is defined to return 0 for -n with nothing following. Whyever.



                                Quoting does make a difference for echo, too, in several cases:



                                var='*'
                                echo $var
                                echo "$var"

                                var="foo bar"
                                echo $var
                                echo "$var"





                                share|improve this answer

















                                • 2




                                  It's not echo, it's the shell. You'd see the same behavior with ls. Try touch '*' some time if you feel adventurous. :)
                                  – Michael Kjörling
                                  Feb 21 '13 at 14:18










                                • That's just wording as there is no difference to the 'if [ ... ]` case. [ is not a special shell command. That's different from [[ (in bash) where quoting is not necessary.
                                  – Hauke Laging
                                  Feb 21 '13 at 15:06












                                up vote
                                0
                                down vote










                                up vote
                                0
                                down vote









                                Empty parameters are removed if not quoted:



                                start cmd:> strace -e trace=execve echo foo $bar baz
                                execve("/usr/bin/echo", ["echo", "foo", "baz"], [/* 100 vars */]) = 0

                                start cmd:> strace -e trace=execve echo foo "$bar" baz
                                execve("/usr/bin/echo", ["echo", "foo", "", "baz"], [/* 100 vars */]) = 0


                                The called command doesn't see that there was an empty parameter on the shell command line. It seems that [ is defined to return 0 for -n with nothing following. Whyever.



                                Quoting does make a difference for echo, too, in several cases:



                                var='*'
                                echo $var
                                echo "$var"

                                var="foo bar"
                                echo $var
                                echo "$var"





                                share|improve this answer













                                Empty parameters are removed if not quoted:



                                start cmd:> strace -e trace=execve echo foo $bar baz
                                execve("/usr/bin/echo", ["echo", "foo", "baz"], [/* 100 vars */]) = 0

                                start cmd:> strace -e trace=execve echo foo "$bar" baz
                                execve("/usr/bin/echo", ["echo", "foo", "", "baz"], [/* 100 vars */]) = 0


                                The called command doesn't see that there was an empty parameter on the shell command line. It seems that [ is defined to return 0 for -n with nothing following. Whyever.



                                Quoting does make a difference for echo, too, in several cases:



                                var='*'
                                echo $var
                                echo "$var"

                                var="foo bar"
                                echo $var
                                echo "$var"






                                share|improve this answer













                                share|improve this answer



                                share|improve this answer











                                answered Feb 21 '13 at 13:57









                                Hauke Laging

                                53k1281130




                                53k1281130







                                • 2




                                  It's not echo, it's the shell. You'd see the same behavior with ls. Try touch '*' some time if you feel adventurous. :)
                                  – Michael Kjörling
                                  Feb 21 '13 at 14:18










                                • That's just wording as there is no difference to the 'if [ ... ]` case. [ is not a special shell command. That's different from [[ (in bash) where quoting is not necessary.
                                  – Hauke Laging
                                  Feb 21 '13 at 15:06












                                • 2




                                  It's not echo, it's the shell. You'd see the same behavior with ls. Try touch '*' some time if you feel adventurous. :)
                                  – Michael Kjörling
                                  Feb 21 '13 at 14:18










                                • That's just wording as there is no difference to the 'if [ ... ]` case. [ is not a special shell command. That's different from [[ (in bash) where quoting is not necessary.
                                  – Hauke Laging
                                  Feb 21 '13 at 15:06







                                2




                                2




                                It's not echo, it's the shell. You'd see the same behavior with ls. Try touch '*' some time if you feel adventurous. :)
                                – Michael Kjörling
                                Feb 21 '13 at 14:18




                                It's not echo, it's the shell. You'd see the same behavior with ls. Try touch '*' some time if you feel adventurous. :)
                                – Michael Kjörling
                                Feb 21 '13 at 14:18












                                That's just wording as there is no difference to the 'if [ ... ]` case. [ is not a special shell command. That's different from [[ (in bash) where quoting is not necessary.
                                – Hauke Laging
                                Feb 21 '13 at 15:06




                                That's just wording as there is no difference to the 'if [ ... ]` case. [ is not a special shell command. That's different from [[ (in bash) where quoting is not necessary.
                                – Hauke Laging
                                Feb 21 '13 at 15:06












                                 

                                draft saved


                                draft discarded


























                                 


                                draft saved


                                draft discarded














                                StackExchange.ready(
                                function ()
                                StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f65624%2fwhy-do-i-need-to-quote-variable-for-if-but-not-for-echo%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