Episode: 2659 Title: HPR2659: Further ancillary Bash tips - 11 Source: https://hub.hackerpublicradio.org/ccdn.php?filename=/eps/hpr2659/hpr2659.mp3 Transcribed: 2025-10-19 07:09:04 --- This is HPR episode 2659 entitled Further Ancillary Mass Tips 11 and in part of the series Bash Crypting. It is hosted by Dave Morris and in about 28 minutes long and Karim and exquisite flag. The summary is making decisions in Bash Part 3. This episode of HPR is brought to you by archive.org. Support universal access to all knowledge by heading over to archive.org forward slash Donate. Hello everybody. This is Dave Morris. Welcome to Hacker Public Radio. So I'm doing another show in my Bash Tips sub-series because they're part of the Bash scripting series. This is the 11th episode on Bash Tips that I've done and this is the third of a group of shows that I'm making about making decisions in Bash. In the last two episodes we saw the types of tests that Bash provides and we look briefly at some of the commands that use these tests. Now we want to start examining the expressions that can be used in the tests and how to combine them. We'll also start looking at string comparisons in extended tests. So let's look at Bash's conditional expressions. So our examples of tests have contained these already but I haven't explained them and I haven't listed them all. What I've done here is to copy the contents of the appropriate section of the GNU Bash Manual. I've put a link in here as well and it's also in the Bash Man page. I thought I would include this in the long notes because it's just useful to have a reference point and finding these otherwise can be a little bit difficult. Certainly reading the Bash Man page is quite a journey because it's huge. So what I've done is I've taken that list, I've reproduced it into the notes but I've added a few explanations where it seemed necessary to make it a bit more detail to explain things a little bit better. I'm not going to read them all out. I'll just sort of whizz through them and refer to specific tests that I think you might need to use in the simpler types of script. So the conditional expressions are used by so-called extended test operators. This is the double open and double close square brackets and also by the test command and the single open and close square brackets which are built in commands and I talked about these in the first part of this group of shows which I've referenced here, 2639. The expressions can be unary or binary. We operate as, take a single argument to the right, we'll see some of these in a minute. Whereas the binary operators take two arguments, one to the left and one to the right. Unary expressions are often used to examine the stages of a file, not all of them though. There are string operators and numeric comparison operators as well. When you are using the extended test operators, the less than sign and greater than sign operators, then comparing two strings in that way, sort lexical graphically using the current locale. So that means bash knows what locale you're using in your operating room with you using the UK locale or you're in France or wherever and it will use the character sets which are appropriate. However, the test command and the single square bracket equivalent uses just plain ASCII ordering when comparing things. Personally, I don't use string comparisons of that sort to determine whether one string is greater than another. Can I guess be useful if you're doing some sort of sorting type comparison, but it's a little bit prone to misunderstanding, I find anyway. The other thing to say is that unless it's otherwise specified, the operators which operate on files follow any symbolic links and operate on the target rather than on the link itself. So if you've got a link to a file and you do some comparison with it, for example, to check whether it exists, then it's not the link that you're, because the link can exist and the file it points to can be can be vanished. So it's not the link exists that you're asking, it's whether the file exists that it points to. Okay, it's getting a bit complex already. So I'm just going to pop through these things relatively quickly as I say. I'm going to skip over a thin number of them. The first one, the minus a followed by the name of a file, that's a check to see if the file exists, but ideally you shouldn't use it because it's ambiguous, I think. I'll come on to wine a minute, but I thought I just mentioned it. It's been deprecated, which I think it's going to be removed at some stage. Minus lowercase d followed by a file name, if the file exists and in is and is a directory, that will return true. And the minus lower case e file is true if the file exists. We've seen that. I've used that in some examples already. There are quite a lot of others relating to files, which I'm not going to go into. There is a minus h1 minus h file is true if the file exists and is a symbolic link. So you can actually find out what things you have in your directory, which are a symbolic link. If you need to, that'll be one that doesn't follow through to the file end. So assuming that you could loop through files and determine which ones were links and then check to see if the file they pointed to existed and on that basis delete things which no longer pointed to a file. I haven't tested this, so it's not one of my examples, but I guess you could do that. Here's one I didn't know before. There's a minus t followed by a number of a file descriptor. I haven't talked about file descriptors, and I can't say I'm more that up to speed with what, with how you work with file descriptors. Something I should maybe do later on, but it can be used to check whether standard in, which is file descriptor 0 or standard out, which is file descriptor 1 are terminals. So you can use minus t0 or minus t1 to ask whether either of these are connected to a terminal. So you could have a job, a piece of work script that was running detached from a terminal and could work out whether or not it was connected through that means, but that's probably more advanced than I should talk about. I quite like minus s followed by a file name, because it checks if a file exists, and whether it has a size greater than 0. So you can do things like check the output file from something. It might exist, but it might be empty, so it's useful to be able to say that looks like the program didn't write anything, and that might be suspicious in a script. Here's one I had not come across before and till I started preparing this list. It's minus v, in the case v, followed by the name of a variable. If the shell variable referenced is set, that is, has been a assigned a value, then it returns true. That might be useful just to find out things about variables. Again, I've not created examples for any of these, and I've not really tested them. I'm just prattling about them off the top of them. My head really. Minus z, followed by a string, or a variable, is true if the length of the string or variable is 0. So I guess you'd put, when it says string, it means a variable substitution in double quotes. So you, that way, you could work out if, particular string that you'd captured from somewhere else, had a length of 0. There's a corresponding minus lowercase n, followed by a string, which is true if the string is non-zero length. So those are the bunch of unary operators. There's a few binary ones. There's one where you can compare two strings. So you'd compare string one, then two equal signs, and string two. You could also use a single equal sign. That's true if the two strings are equal. When you use it with extended tests, this can also perform pattern matching, which I'm going to go into detail about later. The converse of the double equals is exclamation mark equals, that's not equals. Then we come to string one less than string two. That's just about whether it's sorts before string one, sorts before string two, and string one greater than string two, which is the opposite. And the last set is where you have an expression that consists of an argument one, an operator, and an argument two. The operators are things like minus EQ, minus NE. These are all arithmetic binary operators, and they return true if the first argument is equal to, if you think EQ or not equal to, if you use NE, not equal to argument two. So I've already done examples of this in some of the earlier shows in this group. So I thoughtfully they're pretty obvious. They listed that in the notes here, but I'm not going to go into tiny minute detail. So if you want to do things like construct more complex expression where you can catenate or combine the simpler expressions, then you can use an exclamation mark to reverse the effect. So not, that's a not symbol, not operator, followed by the expression. So you could do not minus E, and then some file name, which will check to see if it doesn't exist, return true if it doesn't exist. The order in which these things are, the combined expressions are evaluated, can be changed by putting parentheses around bits of the expression. So it changes the normal precedence of operators. That's really to do with the ordering of not and or, which are sort of Boolean operators. If you want to combine the results of two expressions using an AND operation, then use minus lowercase A. That's where I said it. There's a potential ambiguity with the unary minus A. There's also a minus O for OR, which is also a unary version. But these forms minus A and minus O are only available in when used with test and with the single square brackets. If you are combining expressions and wanting logical ANDs and ORs and you're using extended tests, then you use the double ampersand and the double vertical bar, which I talked about in a slightly different context in the last show. These ones short circuit, unlike the minus A and minus O. So if I explain this in the context of command lists in the last show, so maybe I don't need to go into detail, but the notes do attempt to explain this a little bit. Okay, so that's really all there is to be said about in a formal way about this stuff. So conditional expression examples. Example one, and there's a typical example of a not equals not minus E, I should say, which checks to see if a file exists. So we've got if double open square brackets, exclamation mark, space, minus E, space, then the name of a file in double quotes, close square brackets, semicolon, then we echo, file, not found, and exit with the value of one, and then finish the if, with the FI. So that's just, I'm not going to read out all these examples because it doesn't seem necessary. But this is a typical example of the minus E operator, and it's checking the existence of a file, and if it doesn't exist, it's going to abort the script that it's in. So this is just a snippet. Exiting with one, I think I've said this several times already in previous shows, means that the thing that's calling the script that's maybe calling the script that you're writing can take error action on the basis of whether the called script fails or not. As you will probably recall from the last show, this can also be written as a command list, and I've shown an example of it. I won't read it out and explain any more because I think it's pretty self-evident and there's plenty of information in these notes and the previous ones. So example two is a complete script, though not very interesting one, which checks to see if a particular directory exists, and if not creates it. So the directory that we're interested in is stored in the variable, base, dirt, which is in capital. And then we check to see whether using the minus D unary option operator, whether not in front of it, to see whether the directory exists. If it doesn't exist, then we're going to create it. When you use mkdure, make dure to create that directory. But as an extra twist, the make dure command is followed as part of a command list. So it's followed by the two vertical bars, logical or, and in curly braces, an echo that says fail to create it because the commands failed and exits with the value of one. Otherwise, it will echo created, base, dirt, whatever the value. So I've included that one as a downloadable script that you can grab and mess around with if you want to. Example three is looking at checking the length of the string. The string might be the output from a command or input from the script user, for example. So one way to check to see if the string is empty is using if, and then use an extended test where you compare the parameter substitution expression, dollar, open curly bracket, hash mark, reply, that's the name of the variable, close curly brace. So if you remember from one of the earliest shows in this series, that's how you get a number representing the length of the variable reply in this case. So we compare that with zero using a minus EQ. And then if that is zero, then we echo, please provide a non-empty reply. That's certainly one way to do it, it works, it's fine. Maybe a better way of doing it is to use the minus Z operator against dollar reply and do it the same. So I wrote a little script just to demonstrate the two things. It reads from the standard in to get a message which you can type and it then checks to see if it's empty or not. And it does it using both methods. So I've run it with some test data. In the worst case, please enter a string, it says and I typed okay and then it comes back says you said okay. Then it does it again, please enter a string and I just pressed return, it said please provide a non-empty reply. You can download this one to play with, it's called bash 11 underscore EX2 dot SH. So there's tons more of these types of examples I could go into but I think it's simple enough that you can experiment with these yourself if you're so minded. What I want to talk about now is the bit where we were talking about a string comparison. String comparisons are more powerful than some of the other expressions that we've looked at and they one of the there's another operator that isn't in that list. Remember I copied that list so it's not included there which seems a little odd actually but anyway there's another operator which allows you to do string comparisons using regular expression. Now I'm not going to look at that today and that's coming in in the next show, the next episode in this series because that's one of the most powerful things but also pretty complex. But when we're doing string comparisons in bash it can be a bit more than just simply comparing two strings. We can use pattern matching. When we're comparing strings with test and single square brackets using equal equals or not equals the exclamation mark equals the two strings being about treated as plain strings not surprisingly. However when we use extended test operators that's the double square brackets it's possible to compare the left hand argument with a pattern as the right hand argument. You can't swap them right you can't have a pattern on the left and the string on the right so it's string on the left pattern on the right and I've referenced the canoe bash manual about this with quite a lot of detail. The pattern and pattern comparison stuff was all discussed back in 2007 8 and 2293 which are in this series of bash tips but it was talked about in the context of path name expansion. You might want to go back and look at the the notes if you if you want to use this because this is actually quite powerful. When doing string comparison in an expression like this then the pattern is treated as if the x-t-glob E-x-t-glob option were enabled so you don't have to switch it on you did have to do that with path name expansion if you wanted to get all the fancy stuff but it's here without needing any further work inside extended tests. There's also another option called no case match so you can actually make the pattern case in sensitive if you wish. So you can do some quite sophisticated pattern matching but the pattern must not be quoted. If you do so then it's treated as a simple string and all the pattern magic doesn't work. Now according to the documentation it's possible to quote part of a pattern and that way you can treat pattern meta characters as simple characters but I haven't managed to get that to work as I say in the footnote here. I don't understand what they're getting at and I haven't found any comprehensive documentation that explains this feature so I guess we'll leave that there for the moment. If I find out more I will pass the message on. So here's some pattern matching examples then. So this is example 4 in this show and what we're doing here is we're setting it as just a snippet of code. We're setting a variable animal equal to the string grizzly bear. Then the test is if then double open square brackets dollar animal equal equal star bear. Now there's no quote trying this. This is a pattern asterisk bear and then close close the double square brackets semicolon then we echo first is true we echo detected a type of bear colon then the the name of the animal the contents of the variable. So this it tests for any string which ends with the letters B-E-A-R and that includes a string that just contains the word bear so we'll be saying but that's actually quite powerful. You can you can do in just sort of basic pattern matching that type of comparison can be quite useful. So that's a that's just a simple glob type comparison. Example 5 uses an extended glob in this case these we have a it's another snippet although this one exists as a complete script that you can download and play with. So we've got a variable called STR which is set to the title of this particular show and the comparison we make is between the the variable STR equals equals but it's the pattern that's the that's the the important bit. So I'll concentrate on that. Now this uses an extended pattern that you get with the xt glob usually which consists of a plus sign followed by some stuff in parentheses. That format says one or more occurrences of whatever the pattern is within those parentheses. Well the pattern in this case is a square bracketed pattern. Remember that's the thing you can use for ranges of characters and so forth and inside the square brackets we have a character class, POSIX character class which is ALNUM alpha numeric but you have to write that as square brackets colon ALNUM colon closed square bracket. So that's a character class which you can only use inside a character range expression. That's then followed with a space and a hyphen. So we're saying that one or more of alpha numeric characters spaces and hyphen we're matching with. So since the title is further ancillary bash tips space hyphen space 11 you should match and if you run it you'll find it does. Example 6 is another one of these extended patterns. This one uses an at followed by a pattern list inside parentheses. This one if you do that matches one any one of a list of patterns. The subsidiary patterns that the list of patterns is separated by vertical bar characters. So in the actual script this is another standalone script that you can download is simple though. There's a loop full loop which sets a variable called STR to successively the strings dog pig rat and cat and also a blank string an empty string. The comparison is being made between this STR variable and at open parentheses pig vertical bar dog vertical bar cat closed parentheses. So what actually happens when you run it is you get back the indication that it matched the dog it matched the pig but it didn't match rat because it's not one of those names in that list. It matched cat but it didn't match the empty string. So it's pretty obscure if you've not been brought up to this stuff if you've not learned this then it is quite obscure but I was quite personally I was quite pleased to find that there was this facility in bash and do use it and using it in this context is also extremely powerful I find. I don't have many scripts that do it I have to say but after doing this show I will probably do probably be using it quite a lot. So the last example and before we finish is example 7 and this is another one that you can download bash 11 EX5 it is. This one is making a comparison of a pattern and a variable again and but in this case the pattern is stored in a variable itself just to demonstrate that that's possible. The pattern match is any word that ends with M-A-N and which is longer than three letters. So the expression inside this that's being matched is a plus followed by a parenthesized list of patterns and the patterns in this list was one. It's a square bracketed range expression and inside it is a character class and it's the character class word so it's square brackets call on word call on square bracket and that matches characters which are letters numbers and the underscore. That's then followed is the pattern's not finished yet that's then followed with the word man. So what it's saying is one or more word characters followed by M-A-N is a for loop that goes through a list setting STR2 the values man, in the case woman, German, X-man, romance and an empty string and it's the if checks each one of these dollar STR equals equals dollar pattern. Pattern is what contains the pattern as you'll see if you look at the script and if it matches it says matched and reports the string that matched otherwise it says didn't match and reports that string and it starts off by reporting what the pattern is. So it didn't it didn't match man because man doesn't have a preceding word character matched woman matched German it matched X-man but it didn't match romance because the man didn't end it wasn't at the end of the string and it also didn't match the empty string not too surprisingly. I pointed out in the notes here that the expression that I use the if string that I use gets flagged as an error by the spell check, not spell check, a shell check tool that I use inside VIM where I develop these things and it complains that dollar pattern should be quoted to prevent glob matching. So since that's what we're trying to do it's a little irritating that it does that but then you can't expect it to know what I'm supposed to, it can't read my mind. It's possible with shell check if you want to get into it at all to include a comment in front of the thing that causes a problem that says ignore this effectively ignore error number SC2053 but I won't go into more detail by the number. Okay so we now know that you can do some cool things with string matching using patterns so I hope you find that useful and as I said already we're going to go on to look at the other types of string matching which is using regular expressions which is even more powerful. Okay that's it bye bye. You've been listening to Hecropublic Radio at HecropublicRadio.org. We are a community podcast network that releases shows every weekday, Monday through Friday. Today's show, like all our shows, was contributed by an HBR listener like yourself. If you ever thought of recording a podcast and click on our contributing to find out how easy it really is. Hecropublic Radio was founded by the digital dog pound and the Infonomicon Computer Club and is part of the binary revolution at binrev.com. If you have comments on today's show please email the host directly leave a comment on the website or record a follow-up episode yourself unless otherwise status. Today's show is released on the creative comments, attribution, share-like, free-to-life-sense.