274 lines
23 KiB
Plaintext
274 lines
23 KiB
Plaintext
|
|
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.
|