Being new to some aspects of BASH, I tried to reduce the quantity of scripts by introducing a comparison test into an existing working script.
The script refused to work until I placed [ ] around the actual test. The second test, in the same script, misfunctioned until I removed the [ ] around the second test.
---------------------------- NON WORKING first comparison
5 if $dir="law" 6 then 7 www="law" 8 dir=${file:0:5} 9 else 10 www="www/s" 11 fi
Error message = /root/bin/.us: line 5: law=law: command not found
---------------------------------
WORKING first comparison
5 if [ $dir="law" ] 6 then 7 www="law" 8 dir=${file:0:5} 9 else 10 www="www/s" 11 fi
------------------------------------
NON-WORKING second comparison
15 if [ $file='law00.css' ] 16 then 17 file=$dir/$file 18 echo "css" 19 else 20 file=$dir/$file.php 21 echo "no css" 22 fi 23 #----------------------------
Every comparison in the second test, including wrong comparisons, satisfy the test and caused the 'css' display.
When line 15 was changed to
15 if [ $file='law00css' ]
everything continued to match including items with no 'css' in the file name.
Baffled.
Are C5 BASH scripts restricted to only 1 IF comparison ?
On Fri, Feb 13, 2015 at 11:26 PM, Always Learning centos@u64.u22.net wrote:
Being new to some aspects of BASH, I tried to reduce the quantity of scripts by introducing a comparison test into an existing working script.
The script refused to work until I placed [ ] around the actual test. The second test, in the same script, misfunctioned until I removed the [ ] around the second test.
I think you are missing some very basic concepts here. First, the shell likes to parse things separated by white space. Second, [ is a synonym for test which is a build-in version of /bin/test, so try 'man test' for the syntax of tests. And third, you generally should use double quotes around variables in tests so they continue to exist as an empty string if the variable happens to not be set.
On Fri, 2015-02-13 at 23:46 -0600, Les Mikesell wrote:
I think you are missing some very basic concepts here. First, the shell likes to parse things separated by white space. Second, [ is a synonym for test which is a build-in version of /bin/test, so try 'man test' for the syntax of tests. And third, you generally should use double quotes around variables in tests so they continue to exist as an empty string if the variable happens to not be set.
Thanks for that. I assumed if test 1 worked, so would test 2.
Have re-run test 2 with
16 if [ $file = "law00css" ] 17 then 18 echo $file 19 echo "css" 20 else 21 echo "no css" 22 fi
and got
- '[' law45p07a01 = law00css ']'
- echo 'no css'
no css
- exit
which is correct (for the first time). It seems that following your good advice and plonking spaces around the = has solved the problem.
Thank you very much. Now I can go to bed a satisfied person :-)
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
Try using [[ and ]] rather than [ and ]. Under BASH the double form are reserved words with special meaning to the shell whereas the single form are just a synonym for test. Parsing and splitting rules are different, in particular word splitting and pathname expansion are not performed. Consider:
#!/bin/sh myvar="" set -vx [[ -n $myvar ]] && echo "non-null" [ -n $myvar ] && echo "non-null"
bash-4.2$ ./X [[ -n $myvar ]] && echo "non-null" + [[ -n '' ]] [ -n $myvar ] && echo "non-null" + '[' -n ']' + echo non-null non-null bash-4.2$
Note how in the second case $myvar has been parsed out of existence!
On 14/02/15 05:54, Always Learning wrote:
On Fri, 2015-02-13 at 23:46 -0600, Les Mikesell wrote:
I think you are missing some very basic concepts here. First, the shell likes to parse things separated by white space. Second, [ is a synonym for test which is a build-in version of /bin/test, so try 'man test' for the syntax of tests. And third, you generally should use double quotes around variables in tests so they continue to exist as an empty string if the variable happens to not be set.
Thanks for that. I assumed if test 1 worked, so would test 2.
Have re-run test 2 with
16 if [ $file = "law00css" ] 17 then 18 echo $file 19 echo "css" 20 else 21 echo "no css" 22 fi
and got
- '[' law45p07a01 = law00css ']' + echo 'no css' no css + exit
which is correct (for the first time). It seems that following your good advice and plonking spaces around the = has solved the problem.
Thank you very much. Now I can go to bed a satisfied person :-)
On Fri, Feb 13, 2015 at 11:54 PM, Always Learning centos@u64.u22.net wrote:
And third, you generally should use double quotes around variables in tests so they continue to exist as an empty string if the variable happens to not be set.
Thanks for that. I assumed if test 1 worked, so would test 2.
Have re-run test 2 with
16 if [ $file = "law00css" ]
You still missed the part about quoting variables. You quote plain strings to hold embedded spaces together (or single-quotes to avoid parsing metacharacters). You use double quotes around $variables so they don't disappear completely if the variable isn't set, causing a syntax error. To understand it completely you need to know the order of operations as the shell makes multiple passes over the line, parsing, processing metacharacters, and expanding variables. And I don't know where to find a concise description of that any more.
Once upon a time, Les Mikesell lesmikesell@gmail.com said:
On Fri, Feb 13, 2015 at 11:54 PM, Always Learning centos@u64.u22.net wrote:
16 if [ $file = "law00css" ]
You still missed the part about quoting variables. You quote plain strings to hold embedded spaces together (or single-quotes to avoid parsing metacharacters). You use double quotes around $variables so they don't disappear completely if the variable isn't set, causing a syntax error. To understand it completely you need to know the order of operations as the shell makes multiple passes over the line, parsing, processing metacharacters, and expanding variables. And I don't know where to find a concise description of that any more.
The thing to remember is that originally, the left square bracket [ was an external command (an alias of the "test" command). The shell interpreter just ran commands and tested the output. So, consider everything past the "if" as a single command, and treat it accordingly (so quoting arguments just like you would to "ls" or something).
So, in the above line, $file would be expanded by the shell as part of command argument processing, and it is empty/not set, the command would be run as:
[ = law00css ]
That's invalid syntax. If instead, you call it with $file in quotes:
[ "" = law00css ]
That's valid syntax (and then tests as false).
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
On 14/02/15 16:53, Les Mikesell wrote: <snip>
To understand it completely you need to know the order of operations as the shell makes multiple passes over the line, parsing, processing metacharacters, and expanding variables. And I don't know where to find a concise description of that any more.
man bash, about 900 lines down under "EXPANSION".
On Sat, Feb 14, 2015 at 11:36 AM, J Martin Rushton martinrushton56@btinternet.com wrote:
<snip> > To understand it completely you need to know the order of > operations as the shell makes multiple passes over the line, > parsing, processing metacharacters, and expanding variables. And > I don't know where to find a concise description of that any more.
man bash, about 900 lines down under "EXPANSION".
But it is not 'just' expansions. You need to know the full order of operations with all the steps - word splitting, quote removal, i/o redirection, groupings, etc., some of which is repeated over the line after some of the other steps happen. I think I saw this in an understandable form for the bourne shell back in the 1980's but can't remember it well enough to describe and all the bash docs I've seen are way too convoluted to just see the order of operations as a simple set of steps - that you need to know before any of the rest will make sense.
On 02/14/2015 12:03 PM, Les Mikesell wrote:
On Sat, Feb 14, 2015 at 11:36 AM, J Martin Rushton martinrushton56@btinternet.com wrote:
<snip> > To understand it completely you need to know the order of > operations as the shell makes multiple passes over the line, > parsing, processing metacharacters, and expanding variables. And > I don't know where to find a concise description of that any more.
man bash, about 900 lines down under "EXPANSION".
But it is not 'just' expansions. You need to know the full order of operations with all the steps - word splitting, quote removal, i/o redirection, groupings, etc., some of which is repeated over the line after some of the other steps happen. I think I saw this in an understandable form for the bourne shell back in the 1980's but can't remember it well enough to describe and all the bash docs I've seen are way too convoluted to just see the order of operations as a simple set of steps - that you need to know before any of the rest will make sense.
It's the 4 paragraphs at the start of the "EXPANSION" section:
Expansion is performed on the command line after it has been split into words. There are seven kinds of expansion performed: brace expansion, tilde expansion, parameter and variable expansion, command substitution, arithmetic expansion, word splitting, and pathname expansion.
The order of expansions is: brace expansion, tilde expansion, parameter, variable and arithmetic expansion and command substitution (done in a left-to-right fashion), word splitting, and pathname expansion.
On systems that can support it, there is an additional expansion avail- able: process substitution.
Only brace expansion, word splitting, and pathname expansion can change the number of words of the expansion; other expansions expand a single word to a single word. The only exceptions to this are the expansions of "$@" and "${name[@]}" as explained above (see PARAMETERS).
On Sat, Feb 14, 2015 at 1:13 PM, Robert Nichols rnicholsNOSPAM@comcast.net wrote:
But it is not 'just' expansions. You need to know the full order of operations with all the steps - word splitting, quote removal, i/o redirection, groupings, etc., some of which is repeated over the line after some of the other steps happen. I think I saw this in an understandable form for the bourne shell back in the 1980's but can't remember it well enough to describe and all the bash docs I've seen are way too convoluted to just see the order of operations as a simple set of steps - that you need to know before any of the rest will make sense.
It's the 4 paragraphs at the start of the "EXPANSION" section:
Expansion is performed on the command line after it has been split into words. There are seven kinds of expansion performed: brace expansion, tilde expansion, parameter and variable expansion, command substitution, arithmetic expansion, word splitting, and pathname expansion.
The order of expansions is: brace expansion, tilde expansion, parameter, variable and arithmetic expansion and command substitution (done in a left-to-right fashion), word splitting, and pathname expansion.
On systems that can support it, there is an additional expansion avail- able: process substitution.
Only brace expansion, word splitting, and pathname expansion can change the number of words of the expansion; other expansions expand a single word to a single word. The only exceptions to this are the expansions of "$@" and "${name[@]}" as explained above (see PARAMETERS).
I think that is still an oversimplification because more than expansion is involved and the order related to other steps. When does it do i/o redirection; which things happen before/during starting subshells/pipes; what if you use 'eval', etc.?
On Sat, Feb 14, 2015 at 11:28 AM, Les Mikesell lesmikesell@gmail.com wrote:
On Sat, Feb 14, 2015 at 1:13 PM, Robert Nichols rnicholsNOSPAM@comcast.net wrote:
But it is not 'just' expansions. You need to know the full order of operations with all the steps - word splitting, quote removal, i/o redirection, groupings, etc., some of which is repeated over the line after some of the other steps happen. I think I saw this in an understandable form for the bourne shell back in the 1980's but can't remember it well enough to describe and all the bash docs I've seen are way too convoluted to just see the order of operations as a simple set of steps - that you need to know before any of the rest will make sense.
It's the 4 paragraphs at the start of the "EXPANSION" section:
Expansion is performed on the command line after it has been split into words. There are seven kinds of expansion performed: brace expansion, tilde expansion, parameter and variable expansion, command substitution, arithmetic expansion, word splitting, and pathname expansion.
The order of expansions is: brace expansion, tilde expansion, parameter, variable and arithmetic expansion and command substitution (done in a left-to-right fashion), word splitting, and pathname expansion.
On systems that can support it, there is an additional expansion avail- able: process substitution.
Only brace expansion, word splitting, and pathname expansion can change the number of words of the expansion; other expansions expand a single word to a single word. The only exceptions to this are the expansions of "$@" and "${name[@]}" as explained above (see PARAMETERS).
I think that is still an oversimplification because more than expansion is involved and the order related to other steps. When does it do i/o redirection; which things happen before/during starting subshells/pipes; what if you use 'eval', etc.?
-- Les Mikesell lesmikesell@gmail.com
On my bookshelf is a copy of "Learning the bash Shell" by Cameron Newham & Bill Rosenblatt, http://shop.oreilly.com/product/9780596009656.do ISBN 10:0-596-00965-8
I admit I have not completed reading it for the first time, but it has taught me what I DO know about bash. I would love to compile a list of books others have used to learn what they know about bash. Even if you no longer regularly use what is on your shelf, new community members may be able to locate a copy in a public library and advance their knowledge.
Once upon a time, Les Mikesell lesmikesell@gmail.com said:
I think that is still an oversimplification because more than expansion is involved and the order related to other steps. When does it do i/o redirection; which things happen before/during starting subshells/pipes; what if you use 'eval', etc.?
Try this: "An Introduction to the UNIX Shell" (by the person who wrote the Bourne shell, on which bash is based):
http://people.fas.harvard.edu/~lib113/reference/unix/bourne_shell.pdf
Or, the current version of the standard specification of the POSIX shell, which bash implements:
http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
On Sat, 2015-02-14 at 05:26 +0000, Always Learning wrote:
NON-WORKING second comparison
15 if [ $file='law00.css' ] 16 then 17 file=$dir/$file 18 echo "css" 19 else 20 file=$dir/$file.php 21 echo "no css" 22 fi 23 #----------------------------
Every comparison in the second test, including wrong comparisons, satisfy the test and caused the 'css' display.
When line 15 was changed to
15 if [ $file='law00css' ]
everything continued to match including items with no 'css' in the file name.
I re-ran the script with 'set -x' for
16 if [ $file='law00css' ] 17 then 18 echo $file 19 echo "css" 20 else 21 echo "no css" 22 fi
and received:-
+ '[' law45p07a01=law00css ']' + echo law45p07a01 law45p07a01 + echo css css
On 02/13/2015 11:47 PM, Always Learning wrote:
I re-ran the script with 'set -x' for
16 if [ $file='law00css' ] 17 then 18 echo $file 19 echo "css" 20 else 21 echo "no css" 22 fi
and received:-
- '[' law45p07a01=law00css ']'
- echo law45p07a01
law45p07a01
- echo css
css
Correct. You invoked the test command with a single argument, the string "law45p07a01=law00css". With a single argument, the test is just whether that argument in non-null, which it is. To perform a comparison you need 3 separate arguments (2 operands and an operator), not 1.