Re: select syntax violates the POLA
Date:Thu, 01 Apr 2021 21:33:31 -0400 From:wor...@alum.mit.edu (Dale R. Worley) Message-ID: <874kgpqxlg@hobgoblin.ariadne.com> | I was going to ask why "else {" works, Wrong question. That one is easy. What follows 'else' is a list and the simplest form of a list is a simple command, which starts with a command word, so reserved words are always going to work there, even without the "follows a reserved word' rule. The right question would be why '} else' works. kre
Re: select syntax violates the POLA
On 4/1/2021 16:03, Robert Elz wrote: Partly because if you didn't use the braces, the issue wouldn't have arisen. (And because to most of us it just looks weird, kind of like people who write functions like f() {( commands to run in a subshell )} where the braces do nothing useful at all. and should just be omitted. Heh. Nope -- if it's to be subshelled, I do not use the braces around the parens. that's not an unreasonable use explanation. I find that a consistent style and correct indentation work well enough for this though. I have a consistent style and indentation ("correct" is subjective) :) ... so '} done' should work correctly, always, if the '}' is a reserved word, and a ';' or newline between them should not be needed. Which was why I was kind of taken aback when it failed. kre Thank you, again! --*greywolf;
Re: select syntax violates the POLA
Chet Ramey writes: > Yes, you need a list terminator so that `done' is recognized as a reserved > word here. `;' is sufficient. Select doesn't allow the `done' unless it's > in a command position. Some of the other compound commands have special > cases, mostly inherited from the Bourne shell, to allow it. I was going to ask why "else {" works, since according to the manual page, "{" should not be recognized as a reserved word in this situation. Dale
Re: select syntax violates the POLA
Greg Wooledge writes: > It's amazing how many people manage to post their code with NO comments > or explanations of what it's supposed to do, what assumptions are being > made about the inputs, etc. This leaves us to guess. It seems to be a modern style. When I was learning to program, poorly commented code was considered a failing. But recently, I have had managers object that I put too many comments in. Dale
Re: select syntax violates the POLA
Date:Thu, 1 Apr 2021 14:40:13 -0700 From:Greywolf Message-ID: <354ec4df-c24e-d82a-32ad-788a352a5...@starwolf.com> | Or do you mean my coding style It was that, | (which has been valid for over 25 years)? | (why's everyone bagging on my style and ignoring my original point, the | outlying brokenness, anyway?) Partly because if you didn't use the braces, the issue wouldn't have arisen. (And because to most of us it just looks weird, kind of like people who write functions like f() {( commands to run in a subshell )} where the braces do nothing useful at all. and should just be omitted. But | I started using the curly braces because every (worthwhile) editor has a | paren-match function that's not an unreasonable use explanation. I find that a consistent style and correct indentation work well enough for this though. | I just thought I'd try out select and was astonished when | | select x in ${list}; do { | break; | } done; | | was the only one that failed. Yes, and on second thoughts, the: chet.ra...@case.edu said: | Yes, you need a list terminator so that `done' is recognized as a reserved | word here. `;' is sufficient. Select doesn't allow the `done' unless it's | in a command position. isn't really all that appealing as an explanation. select isn't part of the standard, so its syntax is arbitrary, which means that nothing can really be considered wrong, but while we often think of reserved words (not counting the special cases in case and for statements) as only working in the command word position, that's not how it really is. They work there, they also should work following other reserved words (most of them, but '}' is not one of the exceptions). so '} done' should work correctly, always, if the '}' is a reserved word, and a ';' or newline between them should not be needed. kre
Re: select syntax violates the POLA
On Thu, Apr 01, 2021 at 02:54:55PM -0700, Greywolf wrote: > the requirement > to have ${var[ix]} instead of honouring $var[ix] with regard to arrays > is another one). Before the introduction of arrays, $var[ix] already had a meaning: the value of the "var" parameter, followed by the 4-character string [ix]. Redefining $var[ix] to mean the same thing as ${var[ix]} would have broken existing scripts.
Re: select syntax violates the POLA
Hi, Chet! I've read a lot of your posts long ago, as well! On 4/1/2021 8:57, Chet Ramey wrote: It's more like `select' doesn't inherit some of the shell's special cases. select dir in ${d[@]}; do { break; } done; >> ...but select breaks on the "} done;" syntax Yes, you need a list terminator so that `done' is recognized as a reserved word here. `;' is sufficient. Select doesn't allow the `done' unless it's in a command position. Some of the other compound commands have special cases, mostly inherited from the Bourne shell, to allow it. Firstly, thank you for your concise reply. I discovered that making it '}; done;' as you suggested here worked, shortly after my broken astonishment :). kre mentioned that it's a ksh holdover. This explains a great many things considering how horribly broken I have found ksh to be (and the weak arguments supporting that brokenness; among them, the need to say 'fg %4' instead of merely being able to say '%4'; the requirement to have ${var[ix]} instead of honouring $var[ix] with regard to arrays is another one). Lastly, thanks for not bagging on my coding style. It was somewhat intentional, somewhat accidental when I started using it 25 years back. Being able to paren-match my for-in-do-done, while/until-do-done, if-elif-else-fi has been a timesaver. It not only helps me spot missing closures, it helps me spot missing quotes. [I think the only thing I wish would be enhanced and accepted further was for it to work with case-esac, but that hasn't changed in 25 years, either, and it's minor, so I'm not holding my breath. :) ] Cheers, --*greywolf;
Re: select syntax violates the POLA
On 4/1/2021 9:58, Robert Elz wrote: Date:Thu, 1 Apr 2021 11:36:14 -0400 From:Greg Wooledge Message-ID: | On Thu, Apr 01, 2021 at 01:36:59AM -0700, greyw...@starwolf.com wrote: | > The following is valid shell code: | > | > d=($(ls /usr/src/pkg/*/$1)); | | Syntactically valid, but semantically wrong Mr. Elz! Long time! I remember reading your posts 30 years ago! [ much other ado elided ] | "${d[@]}" with quotes. Again, yes, but in practice here, not needed. | What purpose do the extra curly braces serve? Good question, wondered that myself. Note that if they were omitted the odd syntax issue wouldn't have arisen, as the done would follow "break;" and be recognised. Unfortunately the way bash has implemented arrays, $d[@] doesn't do The Right Thing. Or do you mean my coding style (which has been valid for over 25 years)? (why's everyone bagging on my style and ignoring my original point, the outlying brokenness, anyway?) I started using the curly braces because every (worthwhile) editor has a paren-match function which shows you the matching brace at the other end, and I was tired of searching for which conditional component (while, if, for, and so on) was missing its closing word. This helps me immensely. After all that, and ignoring select's syntax (it was invented by ksh, long long ago, and isn't going to change) it is a dumb interface that can always be coded better using other mechanisms. And when you do that you can allow for much more flexible input than just the line number the select demands. Indeed, I have written such animals from scratch; I just thought I'd try out select and was astonished when select x in ${list}; do { break; } done; was the only one that failed. If you avoid select, and avoid arrays, then any Bourne style shell will work. So, do that. I will also note that the arrays don't give me grief in the least. :) kre Thank you so much for your response! You got exactly what I was after! --*greywolf;
Re: select syntax violates the POLA
On 4/1/2021 9:16, konsolebox wrote: On Thu, Apr 1, 2021 at 11:25 PM wrote: if ((n > 1)); then { echo "Ambiguous dir specification"; exit 1; } else { dir=${d[0]}; } fi; The grouping is unnecessary or should be separate from the first class syntax. Bash is not C or Go and shouldn't be written like it. I didn't ask for your opinion on my coding style. It's worked correctly for 25 years. I have my reasons.
Re: Changing the way bash expands associative array subscripts
On Fri, Apr 2, 2021 at 3:02 AM konsolebox wrote: > > On Fri, Apr 2, 2021 at 2:56 AM Chet Ramey wrote: > > > > On 4/1/21 2:55 PM, konsolebox wrote: > > > > > So to keep compatibility, would this be the right way? > > > > > > if [[ BASH_VERSINFO -ge 6 || BASH_VERSINFO -eq 5 && BASH_VERSINFO -ge 2 > > > ]]; then Here `BASH_VERSINFO -ge 2` should be `BASH_VERSINFO[1] -ge 2` sorry. The condition presumes that the next version that would keep the new behavior of unset to be 5.2. -- konsolebox
Re: Changing the way bash expands associative array subscripts
On Fri, Apr 2, 2021 at 2:56 AM Chet Ramey wrote: > > On 4/1/21 2:55 PM, konsolebox wrote: > > > So to keep compatibility, would this be the right way? > > > > if [[ BASH_VERSINFO -ge 6 || BASH_VERSINFO -eq 5 && BASH_VERSINFO -ge 2 ]]; > > then > > unset() { > > set -- "${@//\[/\\[}" > > set -- "${@//\]/\\]}" > > eval builtin unset "$@" > > } > > fi > > To do what, exactly? To keep this working in both behaviors. declare -A a key='$(echo foo)' a[$key]=1 unset 'a["$key"]' declare -p a # declare -A a=() -- konsolebox
Re: select syntax violates the POLA
Date:Thu, 1 Apr 2021 13:18:07 -0400 From:Greg Wooledge Message-ID: | It's amazing how many people manage to post their code with NO comments | or explanations of what it's supposed to do, what assumptions are being | made about the inputs, etc. This leaves us to guess. Agreed, it is a problem in general. It just happens that in this case I can see exactly what is intended (as I know the environment) so I don't need to guess. | I'm guessing that /usr/src/pkg/*/* is a list of files, not a list of | directories. You're guessing that it's a list of directories. No, I know it is a list of directories. No guessing involved. | In the original code, if it's a list of directories, then we have | "ls firstdir seconddir" which expands to a list of mangled filenames Yes, the original was broken. But that was not what the message was about, it was about the syntax of select. That the rest of the example code wouldn't have worked was kind of not the point, he wasn't asking for assistance with that. What he wanted to know was why his usage of select was giving a syntax error. Chet answered that. | Now, this is obviously just a guess, and I could be extremely wrong | about it. For one thing, they used the variable name "d". That seems | to indicate that it should be a list of directories, rather than a list | of files. More than that, after one of them is selected ("select dir ...") the code did "cd $dir" (you even commented on the lack of quotes in that statement) - so a directory name is clearly what is wanted. Even if you didn't know the environment, this makes that very clear. | So, perhaps their code is simply *broken*, and where they *thought* they | would be getting a list of directories, they actually get a list of | mangled useless filenames, because they forgot the -d argument to ls. Yes. | | > | d=(/usr/src/pkg/*/"$1") | > | > definitely not that, the quotes are wrong in any case | | They are not! They are. As I said, when used $1 is likely to be a pattern 'p5-*' or similar, and it wants to locate all directories named starting with p5- not the one (non-existing) directory named literally "p5-*". | > Alternatively | > d=( $( ls -d /usr/src/pkg/*/$1 ) ) | | This needs quotes around "$1" Again, no it doesn't. Quote everything is a nice starting point, but it does not universally apply. Filename expansion of the $1 is definitely wanted here. | but yes, it's quite possible that this was | their intent. Of course, this is still *broken*, but it only breaks if | one of the directory names contains whitespace characters. They don't. They never will. Of course it could break if IFS was set to something unusual, but since this script is (I presume) not doing that, that's also not an issue. | It's likely | that they didn't happen to have any such directories in their test | environment, and therefore, this bug would go unnoticed for a long time. It would never be noticed, as there will never be a category (the '*') or package (the $1) name containing a space or any other meta-char. There are rules for the names. Too much of pkgsrc (which is what this is dealing with, the NetBSD packaging system, also available for lots of other systems, including various linux variants, Solaris, OSX, and more) depends on the names having specific characteristics for this to ever be changed. | > or just | > d=( $( printf %s\\n /usr/src/pkg/*/$1 ) ) | | This is not much better. It still breaks on whitespace. It would. There is none. | If a list of directories (not their contents) is the desired outcome, | then my original code is perfectly fine: | | d=(/usr/src/pkg/*/"$1") | | This will expand to something like | | d=(/usr/src/pkg/vendor1/netcat /usr/src/pkg/vendor2/netcat) Except that it can't.There can be only one "netcat" in all of the category directories, because those things aren't used (or not really used) for the compiled binary packages. If you restrict the $1 to being a literal string (by quoting it) then there could never be more than one name returned, so using an array, and using select to choose the actual one which is wanted, would be a waste of coding effort. I'm not blaming you for not knowing all this, but like I said, I know the environment, and I can see exactly what is being attempted. | > Just to be sure.Personally I'd do | > | > set -- /usr/src/pkg/*/$1 | > | > and then simply use the positional parameters. | | This still needs quotes around "$1", No, it doesn't. | and it's basically the same as the array assignment -- just less flexible, | and more suited to sh than bash. Exactly. It will work with any shell, bash doesn't need to be available. | > Yes. But it turns out not to matter in this case, as none of | > the names will ever contain anything but "normal" characters | > (no spaces, newlines, ast
Re: select syntax violates the POLA
On 4/1/21 1:18 PM, Greg Wooledge wrote: > On Thu, Apr 01, 2021 at 11:58:13PM +0700, Robert Elz wrote: >> >> | If $1 is not a directory, then you want: >> >> It is a directory, or I'd guess, quite likely a pattern chosen > > It's amazing how many people manage to post their code with NO comments > or explanations of what it's supposed to do, what assumptions are being > made about the inputs, etc. This leaves us to guess. > > I'm guessing that /usr/src/pkg/*/* is a list of files, not a list of > directories. You're guessing that it's a list of directories. > > In the original code, if it's a list of directories, then we have > "ls firstdir seconddir" which expands to a list of mangled filenames > without directory components. And since there's more than one directory, > the array ends up looking like: > > d=(README foo.c foo.h foo README bar.c bar.h foo.c foo.h foobar) > > with repeated filenames, and no knowledge of which filename came out of > which subdirectory. > > I was assuming that this was *not* how it was intended to work, and thus, > I made the guess that /usr/src/pkg/*/$1 expanded to a list of *files*, > and therefore generated a list of full pathnames, e.g. > > d=(/usr/src/pkg/applesauce/Makefile.PL /usr/src/pkg/bananabread/Makefile.PL) > > and then the user can make a meaningful selection from such a list. > > Now, this is obviously just a guess, and I could be extremely wrong > about it. For one thing, they used the variable name "d". That seems > to indicate that it should be a list of directories, rather than a list > of files. > > So, perhaps their code is simply *broken*, and where they *thought* they > would be getting a list of directories, they actually get a list of > mangled useless filenames, because they forgot the -d argument to ls. > >> | d=(/usr/src/pkg/*/"$1") >> >> definitely not that, the quotes are wrong in any case > > They are not! The quotes are wrong, *iff* the intent is that people should be allowed to pass quoted globs as $1 and let those be expanded to produce d=() Which, I can see where Robert is coming from in assuming this. >> (but apart from >> that, if filename expansion happens in array assignments (it doesn't in >> normal ones, and I dislike arrays, so ...) then without the quotes that >> might work. >> >> Alternatively >> d=( $( ls -d /usr/src/pkg/*/$1 ) ) > > This needs quotes around "$1" but yes, it's quite possible that this was > their intent. Of course, this is still *broken*, but it only breaks if > one of the directory names contains whitespace characters. It's likely > that they didn't happen to have any such directories in their test > environment, and therefore, this bug would go unnoticed for a long time. > >> or just >> d=( $( printf %s\\n /usr/src/pkg/*/$1 ) ) > > This is not much better. It still breaks on whitespace. > > If a list of directories (not their contents) is the desired outcome, > then my original code is perfectly fine: > > d=(/usr/src/pkg/*/"$1") > > This will expand to something like > > d=(/usr/src/pkg/vendor1/netcat /usr/src/pkg/vendor2/netcat) > >> Just to be sure.Personally I'd do >> >> set -- /usr/src/pkg/*/$1 >> >> and then simply use the positional parameters. > > This still needs quotes around "$1", and it's basically the same as > the array assignment -- just less flexible, and more suited to sh than > bash. But certainly, this is viable in many cases. > >> Yes. But it turns out not to matter in this case, as none of >> the names will ever contain anything but "normal" characters >> (no spaces, newlines, asterisks, ...) > > Famous Last Words™ It's not too much of a leap if the names are required to be NetBSD ports tree names of packages, which presumably ban such characters and enforce that ban in their tooling. It's not something one would be wise to get into the habit of doing elsewhere, and the quotes don't hurt, so why not add them... unless, again, we assume the intent of the script is in fact to pass in globs. These "Famous Last Words™" are really just a fancy way to repeat your initial, valid objection that the OP gave zero comments or explanation and therefore you're left to guess at the intention without having any contextual knowledge about what /usr/src/pkg is or why one would want to cd into any of them and run make. I suspect Robert does know quite a bit about it though. :) -- Eli Schwartz Arch Linux Bug Wrangler and Trusted User OpenPGP_signature Description: OpenPGP digital signature
Re: select syntax violates the POLA
On Thu, Apr 01, 2021 at 11:58:13PM +0700, Robert Elz wrote: > > | If $1 is not a directory, then you want: > > It is a directory, or I'd guess, quite likely a pattern chosen It's amazing how many people manage to post their code with NO comments or explanations of what it's supposed to do, what assumptions are being made about the inputs, etc. This leaves us to guess. I'm guessing that /usr/src/pkg/*/* is a list of files, not a list of directories. You're guessing that it's a list of directories. In the original code, if it's a list of directories, then we have "ls firstdir seconddir" which expands to a list of mangled filenames without directory components. And since there's more than one directory, the array ends up looking like: d=(README foo.c foo.h foo README bar.c bar.h foo.c foo.h foobar) with repeated filenames, and no knowledge of which filename came out of which subdirectory. I was assuming that this was *not* how it was intended to work, and thus, I made the guess that /usr/src/pkg/*/$1 expanded to a list of *files*, and therefore generated a list of full pathnames, e.g. d=(/usr/src/pkg/applesauce/Makefile.PL /usr/src/pkg/bananabread/Makefile.PL) and then the user can make a meaningful selection from such a list. Now, this is obviously just a guess, and I could be extremely wrong about it. For one thing, they used the variable name "d". That seems to indicate that it should be a list of directories, rather than a list of files. So, perhaps their code is simply *broken*, and where they *thought* they would be getting a list of directories, they actually get a list of mangled useless filenames, because they forgot the -d argument to ls. > | d=(/usr/src/pkg/*/"$1") > > definitely not that, the quotes are wrong in any case They are not! > (but apart from > that, if filename expansion happens in array assignments (it doesn't in > normal ones, and I dislike arrays, so ...) then without the quotes that > might work. > > Alternatively > d=( $( ls -d /usr/src/pkg/*/$1 ) ) This needs quotes around "$1" but yes, it's quite possible that this was their intent. Of course, this is still *broken*, but it only breaks if one of the directory names contains whitespace characters. It's likely that they didn't happen to have any such directories in their test environment, and therefore, this bug would go unnoticed for a long time. > or just > d=( $( printf %s\\n /usr/src/pkg/*/$1 ) ) This is not much better. It still breaks on whitespace. If a list of directories (not their contents) is the desired outcome, then my original code is perfectly fine: d=(/usr/src/pkg/*/"$1") This will expand to something like d=(/usr/src/pkg/vendor1/netcat /usr/src/pkg/vendor2/netcat) > Just to be sure.Personally I'd do > > set -- /usr/src/pkg/*/$1 > > and then simply use the positional parameters. This still needs quotes around "$1", and it's basically the same as the array assignment -- just less flexible, and more suited to sh than bash. But certainly, this is viable in many cases. > Yes. But it turns out not to matter in this case, as none of > the names will ever contain anything but "normal" characters > (no spaces, newlines, asterisks, ...) Famous Last Words™
Re: select syntax violates the POLA
Date:Thu, 1 Apr 2021 11:36:14 -0400 From:Greg Wooledge Message-ID: | On Thu, Apr 01, 2021 at 01:36:59AM -0700, greyw...@starwolf.com wrote: | > The following is valid shell code: | > | > d=($(ls /usr/src/pkg/*/$1)); | | Syntactically valid, but semantically wrong. Greg managed to comment on everything in the original message, except the actual point of it ... Chet answered that, so I won't bother, but: | If $1 is not a directory, then you want: It is a directory, or I'd guess, quite likely a pattern chosen to find where the package that is wanted lives. The '/*/' represents a category grouping, and it is not unusual to not know which one applies to any particular package. My guess is that $1 might be something like *sed* to find any package with "sed" in its name. Or p5-* to select from the perl5 (script) packages. So: | d=(/usr/src/pkg/*/"$1") definitely not that, the quotes are wrong in any case (but apart from that, if filename expansion happens in array assignments (it doesn't in normal ones, and I dislike arrays, so ...) then without the quotes that might work. Alternatively d=( $( ls -d /usr/src/pkg/*/$1 ) ) or just d=( $( printf %s\\n /usr/src/pkg/*/$1 ) ) Just to be sure.Personally I'd do set -- /usr/src/pkg/*/$1 and then simply use the positional parameters. | If $1 is supposed to be a directory, and therefore you want only filenames | and not full pathnames in the array, It is, but he almost certainly wants the directory names, not the contents of the directories. He's going to want the full pathname so once selected, he can cd to it. | > cd ${dir} && | | Quotes. Yes. But it turns out not to matter in this case, as none of the names will ever contain anything but "normal" characters (no spaces, newlines, asterisks, ...) But that's the cd which requires dir to be the full pathname. | > make clean && { | > make update || | > make install; | > } | | Is the inner || supposed to be && by chance? Probably not. This is "if the package is already installed, update it, if not, install it". "make update" will succeed and update the package if needed, if it is installed already, otherwise it will fail. In that case make install will install it (it would fail if a some other version of the package was already installed). (The preceding "make clean" just gets rid of the left overs from any previous compilation, but will fail if there's insufficient permission, or similar, in which case attempting an update or install would also fail). | > if ((n > 1)); then { | > select dir in ${d[@]}; do { | > break; | > } done; | > } | | "${d[@]}" with quotes. Again, yes, but in practice here, not needed. | What purpose do the extra curly braces serve? Good question, wondered that myself. Note that if they were omitted the odd syntax issue wouldn't have arisen, as the done would follow "break;" and be recognised. After all that, and ignoring select's syntax (it was invented by ksh, long long ago, and isn't going to change) it is a dumb interface that can always be coded better using other mechanisms. And when you do that you can allow for much more flexible input than just the line number the select demands. If you avoid select, and avoid arrays, then any Bourne style shell will work. So, do that. kre
Re: select syntax violates the POLA
On Thu, Apr 1, 2021 at 11:25 PM wrote: > > if ((n > 1)); then { > echo "Ambiguous dir specification"; > exit 1; > } > else { > dir=${d[0]}; > } fi; The grouping is unnecessary or should be separate from the first class syntax. Bash is not C or Go and shouldn't be written like it. -- konsolebox
Re: select syntax violates the POLA
On 4/1/21 4:36 AM, greyw...@starwolf.com wrote: Bash Version: 5.1 Patch Level: 4 Release Status: release Description: The 'select' directive's syntax does not correspond to the rest of the shell's syntax. I am not sure if this is by design; if it is, let me know and I'll go away. It's more like `select' doesn't inherit some of the shell's special cases. if ((n > 1)); then { select dir in ${d[@]}; do { break; } done; } else { ... ...but select breaks on the "} done;" syntax Yes, you need a list terminator so that `done' is recognized as a reserved word here. `;' is sufficient. Select doesn't allow the `done' unless it's in a command position. Some of the other compound commands have special cases, mostly inherited from the Bourne shell, to allow it. -- ``The lyf so short, the craft so long to lerne.'' - Chaucer ``Ars longa, vita brevis'' - Hippocrates Chet Ramey, UTech, CWRUc...@case.eduhttp://tiswww.cwru.edu/~chet/
Re: select syntax violates the POLA
On Thu, Apr 01, 2021 at 01:36:59AM -0700, greyw...@starwolf.com wrote: > The following is valid shell code: > > d=($(ls /usr/src/pkg/*/$1)); Syntactically valid, but semantically wrong. If $1 is not a directory, then you want: d=(/usr/src/pkg/*/"$1") If $1 is supposed to be a directory, and therefore you want only filenames and not full pathnames in the array, then we can arrange for that as well, but it's slightly more tedious. Using ls here is wrong, and will break. It's Pitfall #1 on my list for a reason. https://mywiki.wooledge.org/BashPitfalls#pf1 > cd ${dir} && Quotes. > make clean && { > make update || > make install; > } Is the inner || supposed to be && by chance? > if ((n > 1)); then { > select dir in ${d[@]}; do { > break; > } done; > } "${d[@]}" with quotes. What purpose do the extra curly braces serve? Do you want them just because you think they look pretty? > until [ -f ${tmpfile} ]; do { Quotes. And again, those extra curly braces don't serve any purpose. > for dir in ${d[@]}; do { Quotes.
select syntax violates the POLA
Configuration Information [Automatically generated, do not change]: Machine: x86_64 OS: netbsd Compiler: gcc Compilation CFLAGS: -O2 -I/usr/local/include -D_FORTIFY_SOURCE=2 -I/usr/include uname output: NetBSD eddie.starwolf.com 9.99.81 NetBSD 9.99.81 (EDDIE) #9: Tue Mar 23 19:13:25 PDT 2021 greyw...@eddie.starwolf.com:/sys/native/compile/EDDIE amd64 Machine Type: x86_64--netbsd Bash Version: 5.1 Patch Level: 4 Release Status: release Description: The 'select' directive's syntax does not correspond to the rest of the shell's syntax. I am not sure if this is by design; if it is, let me know and I'll go away. The following is valid shell code: d=($(ls /usr/src/pkg/*/$1)); n=${#d[@]}; if ((n > 1)); then { echo "Ambiguous dir specification"; exit 1; } else { dir=${d[0]}; } fi; cd ${dir} && make clean && { make update || make install; } I'd like to replace the first part of the if/else/fi with: if ((n > 1)); then { select dir in ${d[@]}; do { break; } done; } else { ... ...but select breaks on the "} done;" syntax that works with, e.g., while sleep 5; do { date; } done; until [ -f ${tmpfile} ]; do { sleep 5; } done; for dir in ${d[@]}; do { echo ${dir}; } done; This violates the POLA, from my point of view. Repeat-By: d=($(ls /usr/src/pkg/*/$1)); select dir in ${d[@]}; do { break; } done;