Episode: 2448 Title: HPR2448: Useful Bash functions - part 3 Source: https://hub.hackerpublicradio.org/ccdn.php?filename=/eps/hpr2448/hpr2448.mp3 Transcribed: 2025-10-19 03:15:45 --- This in HBR episode 2448 entitled Useful Mash Function, Part 3 and in part of the series Mash Crypting. It is hosted by Dave Morris and in about 34 minutes long and can in an exquisite flag. The summary is a few more possibly useful Mash Functions are discussed. This episode of HBR is brought to you by an honesthost.com. At 15% discount on all shared hosting with the offer code HBR15 that's HBR15. Better web hosting that's honest and fair at an honesthost.com. Hello everybody, my name is Dave Morris. Welcome to Hacker Public Radio. Today I am doing the third show in the series about Bash Functions. I think the series will stretch to a fourth episode but no more than that. I'm not sure how interesting this is to people. If I get any positive feedback I might do more but I suspect that won't happen. This one covers three functions I think might be of interest. They are somewhat more advanced than the earlier shows I've done in this series. You might find that there's things that you can learn from it. You might also see some pretty glaring errors that I have missed so feel free to point them out and comment on these or criticize them however you see fit. These are things I've written for my own use and I use them on a pretty much a daily basis. The first one is called read underscore value and it's simple purpose is to inner script to output a prompt and read a string. You could do that with just a simple read command there's a read command in Bash but I wanted to be able to do away with all the fafing around of writing read statements and checking their results and so on and so forth so I wanted to simplify it so it nominates a variable into which the result is going to be put. I've got a typical example of a call you just type you can run these on the command line but you have to source them first so it is the command the Bash command is source S O U R C E and then the name of the file containing the bit of Bash the function and I've included these functions as individual files with this show so you can fiddle around with them if you're so minded. So given that you've sourced it so that Bash knows this as a function you would simply type on the command line read underscore value and then some prompt string in quotes and I've put an example what is your name question mark and then the next argument is the name of a variable which I've just called name. So this will prompt what is your name you type in a name press return and then the thing exits you can then echo that variable dollar name and you get back the name that you type not not particularly well-chattering it does take a default as well as the third argument so there's another example here that shows read value being used to ask the question where do you live it's going to put the answer into a variable called country and the default is USA so it says where do you live USA and you can delete that and put another value in instead. So my long show notes is very little in there in the short notes goes into quite a lot of detail it shows you the source of this particular function it's not particularly complex it's only 38 lines long and there's not a huge lot in it but as with a lot of bash things there's quite a lot of subties in the way it does stuff it does have some similarities with the functions I've talked about before there was one called yes underscore no which I talked about in earlier episodes and I've pointed to those in the links if you want to go and check them now this one wants to return a result to the caller but bash functions by default can't do that they can't easily return stuff to the level to the script or to the command line that you're calling them from when you issue a return command within the function you can only give it a number and the usual convention is to return zero to show that it was the result was true and return one for false others use other numbers to signal other things you can't send back strings this does that but it does it in a rather roundabout way you can write your functions so that they always write to the same global global variable so you could do something like write a variable called maybe read underscore value underscore result or something like that and then you could call it and then get back the look at that variable but the trouble is it gets quite messy I have worked in in systems where that really was the only way to achieve it there was no hand back type mechanism used to write to global variables that were visible after you'd run the function procedure or whatever the hell it was that took some management it's not impossible but you can do do do better things with bash well it's not entirely obvious so like I said the main purpose of this particular function is to call the bash built in command read and we want to be able to deal with a default value and if you look at the source of the function you'll see that in lines 18 to 20 it's numbered in the listing then you can see there that we we set a default value from the default that's been handed to the function if there is one so it checks to see if the the variable called default is non zero on zero length not empty and if so then it sets it to contain an option to the read command the read command itself is performed on line 26 but it's a little bit more complicated is not just a plain read we call it in the command eval eval what this does is it allows you to compose a command in a string and then it runs that command for you when it allows you to do more complicated things the way that you've built that command string so I mean this particular case the string that's being given to eval is a read command with a minus r option which controls how it deals with backslashes minus e and then if there's a default then that will be minus i in the string that the default value was minus p gives you the prompt and then it finishes off with the variable into which the the answer is to be written and in this particular case is written to a variable called var so if you were just using read on it so you could you could just type these things out but since we're trying to run it in a function where some of these things are being handed through to it by the function we need to go to this extra length running it in eval if you try to do this without using so if you'd written that command in the function read minus r space minus e dollar default minus p an enclosed dollar prompt space of r then what would happen is that the contents of default would not be parsed by by bash the difficulty is that when bash runs the command there are certain strings that it will expect and these in the in the case of the prompt they will have been filled by parameter substitution but there isn't a thing that causes parameter substitution on the command line it's it's not straightforward to make a command up from bits which are held in variables because bash doesn't go and substitute the variables into the the command string itself it would need to do it twice we need to look at it first to see what the command was and so forth and then it would it would need to or maybe it would first need to do all the the the substitutions and then it would need to go back and execute the command it doesn't do that but when you use eval it does you get two two scans of it this is quite hard to understand at least I think it is I certainly have struggled over this in my earlier days of using unix and stuff so what I did here was I ran this read value function using one of the features of bash which is to trace what it's doing you do that with the command set space hyphen x that sets on tracing of commands then you type read what is your surname blah blah blah and you then see all of the steps that are going on as the contents of that function are being executed each line is preceded by a plus sign and you can see something about what it's doing so for example the function itself starts with a local command where it sets the variable prompt to whatever the first positional parameter is dollar one well the first position parameter is the prompt and it is what is your surname and you actually see it being set to that in this this trace info I won't read all of this stuff but the really two of the interesting things anyway are that you can see the default value being set to hyphen i and then the string not provided which is what was what was used on the command line and then you can see the eval being issued where it is the read command that's being assembled consists of this hyphen i quote not provided there's a lot of backslashes in here because there's quotes within quotes and stuff and that's all done automatically by bash it gets a little confusing but then there's a line plus plus it begins to two pluses then you see the read being executed after it's been assembled with eval and then you get the the prompt what is your surname and I actually typed an answer to this and you can see the the answer being dealt with come on to the other eval in a moment I then turn off this tracing thing which you do the set plus x and then echo the value that I get back in the variable surname and it is the the name that I typed the second eval in the function see it's actually even there's only 38 long it lines long it's actually a little bit more complicated actually before I go on to that I should mention that after the eval line 27 there is a thing that says res equals then in quotes dollar question mark dollar question mark is a built in variable inside bash which gives you the result of the last command now if that last if that last command was not zero return to non zero these are these are numeric responses then it means that in the case of read the person typing stuff has hit control d which is end of file which will of course read to abort so it's checking this and if it if the reads are borted it says so it sends an echoes a message saying read a borted and then returns a false value which is one I think I did that in the previous examples so but I thought it's worth mentioning again this is just standard boilerplate stuff that one does in in bash scripts so the final eval which is on line 36 this is the bit where we're trying to return a value back to the the caller now inside the function we have collected the name or whatever it is that's been requested of the user of the of the script and we stored it in a in a variable called var the AR but there's a variable called output name which was set from second position parameter of the function and that's the name of the variable in the calling environment which is to receive the answer so how do you do that well one way of doing that is to make up a command which is the name of the the variable equals the value that's been collected so you'll see the line line 36 says eval then in double quotes dollar output name equals single quote dollar var so what that will do is we'll construct the command and whatever output name was surname in my example equals and then whatever was typed in and if you look at the listing of the trace you can see eval surname equals and then the name I typed in and you get another line after it which is the actual command so the eval shows the command that's been constructed and the next line with two pluses beginning it at the start it shows surname being set to the string now surname is going to be a variable which is not local to the the function it's actually a global variable but the point is that you can define which variable it's going to be it's not going to be a fixed one against the answer okay I was quite a long winded explanation of something that looks simple but is there's there's layers so the second function need these three form group of like things things that are used together this one's called check value it's designed to be used alongside this read value it takes two arguments one is a value that you want to check it actually it doesn't take two arguments it takes N arguments there's always a first one and then there's any number of further ones the first one is a value that you want to check so it'd be something like the name that was returned in the previous example and then it's followed by any number of regular expressions bash regular expressions that to be used to to check validity the first thing the function does you can see it setting the local variable whose name is value seem like a good idea at the time but it sounds silly when I read it out anyway it's setting it to the first positional parameter now there is a there is a digression here because the way it does this we talked about this I did a series way back where I talked about parameter substitution and the magic you can do in bash with parameter substitution and the version of it here is different from the versions I talked about back in that show so you're seeing value equals then in double quotes dollar open brace one that means parameter one then a question mark and then a string usage call on blah blah blah and then close brace closed double quotes what that means is substitute in the contents of positional parameter one and put it into value but if parameter one is missing you just call check value without any arguments at all say then or it's a null value then you might want to take action and abort things to say you call this wrong there's a mistake here or something and that's what this does but the format that I talked about back in that that show way back when was where you had the name of the the parameter which is just one in this case followed by a colon a question mark and then some some string the string was used as an error message and the thing would abort if the parameter was null or unset but the variant I'm using here misses out the colon and what that does is it checks to see just it just checks existence not whether it's empty because there are times when we might want to check a value which is empty and it's perfectly valid for it to be empty so the first thing after this that it does is on line 15 there's a shift command and what shift does is it takes the it throws away the first argument in the the list of positional parameters throws away the first one so it throws away the one that's referred to as dollar one that's because we've got a copy of it we don't want it anymore but we are going to be dealing with the remaining ones so we want to be able to uh to just get them on their own so line 16 to 18 it checks in an if command the value dollar hash dollar hash is bashes record of the number of positional parameters so if that that value is zero then it wants to we want to flag it as an error because we needed some regular expressions to compare things with so put out an error message and return a value false then the next thing is a while loop which begins at line 24 and runs up to line 30 of the function and it just processes each of the positional arguments until aren't anymore it's using the test dollar hash number of positional parameters greater than or equal to one so when that becomes false then it will stop inside the loop it's doing a comparison of the value that came in as argument one to the function against dollar one which is now the first which is the the first positional parameter and it's using regular expression matching now i haven't really talked much about bash regular expressions i plan to do that in another show rush if there's a match then there's a variable called matches which is incremented then after the if command then there's another shift so that just that then that throws away the positional parameter that's just checked it's treating it as a stack it will just go through one after the other and it will delete them out of its stack as it goes as it uses them so once that loop is finished then the match is variable will be zero or non-zero if it's zero it means nothing matched and in that case we return false if there were any matches at all we return true and that's lines 35 onwards is the if then else thing and that's it again it's pretty simple but there's there's elements of a bash magic that you need to understand to be able to fully appreciate what it can do now what i did was in the notes i made an example of how you use this i showed the source commands for read value and check value you need to do them once in a in a session makes these things available to the bash then i declare a temporary function of called demo and into functions are just a name to a brackets to parentheses and then some statements in curly braces in the temporary function the demo function is a variable called name which is set to nothing then read value is called in a loop this is an until loop i've never really talked about loops in great details in bash but maybe i should cover them in another series at some point anyway it's just a type of test and read value returns a true or false remember and the read value is is being called with the prompt what is your first name and it's going to store this in a variable called name and it can cantonate that command with two amberzans and with a test the test simply checks to see test is a is a built-in command which is sort of used behind the scenes in in a sort of normal test you see inside square brackets or double square brackets so it's checking using the minus n option minus n the string that follows checks you whether it's non non zero non no not empty i suppose so basically that loop continues running until a name is is provided turn if you in fact if you hit control d it will re re loop because nothing was returned and if you if you don't type anything at all just hit return then it will continue to loop so that loop just ensures you get a name then it's followed with check value inside an if check value uses the contents of name as the first argument and then there are two regular expressions if there's a match it simply echoes hello and then the contents of name if there's no match then it says that name isn't valid the two regular expressions are pretty simple the first one matches any sequence of upper and lower case letters one or more no spaces no numbers just uppercase and lowercase letters that's all that will accept the second one was designed to take simple hexadecimal sequences so hexadecimal you normally write them as zero x to show that they are hexadecimal and it accepts a lower uppercase x after the zero and then it will take any of the hexadecimal digits which is a to f upper lowercase zero to nine and one or more of those so show it being run it only does one for each one once it gets a name then it exits so if you run demo what's your first name you type in a name and it says hello and it works if the name is just simple simple alphabetics it also works if you type zero x one one one but if you type in what's your first name gym 42 it says now that's not valid because we're not catering for new numbers in digits and in names you could do of course but I thought it was quite amusing that you could type as a name zero x d a d 42 and that was accepted but if you didn't put the zero x on the front d a d 42 dead 42 is not valid so you can do some quite sophisticated things with it so the final one is really just a concatenation of the previous two this one is called read and check and it uses read value to read value and uses check value to check it and it just loops until it gets a valid reply or a control d to abort it as it's arguments it expects a prompt string which can't be null it expects the name of a variable to receive the result which again can't be null it will take a default value which is optional but if you don't provide one you do need to put a placeholder in there like open closed quotes just as a null string and then after that it takes any number of regular expressions so it's you can tell it's a combination of all of there all of the above so the first thing it does is to call read value online 16 with the relevant parameters so prompt and output name the names I've called them and a default if there is one so if the result of that is false it's actually if not read value then it will it will exit the function with a with a false value so the the case where that would happen would be if control d was was hit so we need to check otherwise control it it's just going to fall through it and do the rest of the work in the function if you are trying to abort it so after that read value it will run shift to clear away the first three arguments in the positional parameters list shift can take a number which is the number of things to clear away they could type shift shift shift but shift three is more efficient it takes less typing then there's a loop another until loop from lines 20 to 26 and it checks it's running check value to check the answer that came back from read value and match it against a list of regular expressions now this is done in a in a way you might not have seen the first argument to check value is consists of in double quotes dollar open brace exclamation mark output name close brace close quote what that means is it's doing a an indirect so-called indirect expansion now output name is a variable which holds the name of a variable so remember the the thinking behind read value was that you gave the function the name of a value into which was to write stuff so if we want to do further checks on that value inside this function we can't simply use output name because output name is just the name of another variable a global variable in the callers environment so use this indirect expansion and what it means is examine the context of output name and use what is there as the name of a variable whose value we require so it's it's doing a sort of two level reference indirect expansion is the term that's used so that value is being passed to check value for checking and the other argument which in quotes is dollar at is a way of representing all of the positional parameters to the the function so remember we deleted the first three so we're left with however many regular expression were provided now if check value doesn't return true then the until will loop and because it returned false that it must not matched so the an arrow message is generated in valid input and it shows the the using this indirect expansion again the value that was that was given to the the read value earlier saying this is not valid and then it calls read value again in another if in the same way as before if read value it the second time round is given a control d then it will also exit from the loop and from the function once the loop ends in a in after a valid check the function will return zero which is true so i've given an example of how you might call this i actually wrote this to be originally to be used in a function that i in a script that i used to manage my hpr submissions like this one and so my example i put the the source commands for each of the the files containing these functions you wouldn't do this normally you'd have them in the in the script that you were preparing most likely and then it calls read and check with the prompt enter slot this would be asking for a for an hpr q slot number the variable that's going to be put into is called slot there's no default so we give it a blank string an empty string and then the only regular expression that's matching against is the string hpr in lower case followed by a number between not nine and one or more of those the hpr square bracket zero hyphen nine closed square bracket plus string would match anything any hpr show number but we want also to allow this function to receive this call of the function to be happy with a null response so that string was put into parentheses followed by a question mark and what the question mark is it says whatever the expression that precedes me which in this case is a compound thing in race in parentheses this it i'd be prepared to accept a zero or one of so the equivalent of having nothing you could have done two regular expressions one one without the the parentheses in the question mark and the next one being simply an empty string in all of these cases have put an anchor a start of string an end of string anchor either end so i would put put that in as well with nothing in between that just helps to deal with it would reject cases where they were leading or trailing spaces you could get cleverer and actually take allow those leading and trailing spaces if you wanted to but then the thing the other end would need to trim them off anyway running it i typed in hpr 42 with capital and it comes back in valid input hpr 42 because we're only catering for lowercase and it asks again and i just hit return and then ends quite happily because that's a valid expression echo the contents of slot but i put slashes either side of it just so you can tell there's nothing between the slashes because what you get back is two slashes in succession so the thing is empty so that's that's a sort of stuff that you can do with it like i said i've certainly used these i use them all the time and not only in my show submission stuff but a lot of other things they're not a hundred percent bulletproof i have to say as i was preparing this i found a bug in in one of them so you know they're they're not they're not perfect then very little in software is i don't know what would happen if you when you gave it a regular expression when you give check value or read and check a bad regular expression of whether things that could make it blow up it certainly make it misbehave but you know there's there's no validation of the regular expressions the whole business of passing information back from a function using eval is messy it does work but it's messy but there's a there's a new feature i think relatively new in bash called name ref which allows you to do this in a cleaner way but i'm not going to look at that here we've already spent a lot of time on this one so if you if you liked it or or you can think of any improvements or you thought it was rubbish or whatever you wish i'd stop doing this and go away and let me know because all feedback is useful so you can see in the links links to the previous shows on this subject and that there are downloadable versions of the the functions and of the trace if you want i'm not quite sure why you would but there you go all right that's it then bye bye you've been listening to hecka public radio at hecka public radio dot 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 hecka public radio was found by the digital dog pound and the infonomicon computer club and it's part of the binary revolution at binref.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 stated today's show is released on the creative comments attribution share a light 3.0 license