6 ms·
The Bourne shell lets you set variables in if expressions
- 1337shadow 5y agoLike many other languages such as PHP, Python, JS, Go ...
- wrnr 5y agoThere is legit use of this in the wild, my favourite example is from prosemirror: https://github.com/ProseMirror/prosemirror-example-setup/blob/master/src/keymap.js#L67 https://github.com/ProseMirror/prosemirror-example-setup/blo...
- rwmj 5y agoAs a regular shell user I find this quite surprising. The article omits the important point that the shell variable persists after the conditional expression: $ foo= ; if foo=bar; then echo $foo ; fi ; echo $foo bar bar However this work as expected. foo is only set as an environment variable for the duration of the command (true) in the condition. $ foo= ; if foo=bar true; then echo $foo ; fi ; echo $foo (prints two blank lines) (My examples above use bash rather than the plain Bourne shell). Now I'm wondering if the shell special-cases this. Edit: No it doesn't. It just executes the command as normal using execute_command in the same context as the "if" statement, so any assignments take effect in the script, not in any sub-context. https://git.savannah.gnu.org/cgit/bash.git/tree/execute_cmd.c#n3686 https://git.savannah.gnu.org/cgit/bash.git/tree/execute_cmd....
- twooster 5y agoYou should almost always be running Bash in `-e` (exit-on-error) mode. This necessitates precisely the construct mentioned in this article. For example: set -e var="$( false )" if [ $? -eq 0 ] ; then echo Ok: "$var" else echo Not ok: $? fi If you run this program, neither "Ok" or "Not ok" will be echoed, because the program will exit with an error on the `var=` line. (Not to mention the $? on the not-ok line won't work because it will be the exit code of the `[` test command in the conditional, not the exit code of the captured subshell command). Instead the following will work: set -e if var="$( false )" ; then echo Ok: "$var" else echo Not ok: $? fi Note that this will _not_ work: if ! var="$( false )"; then echo Not ok: $? fi Your output will be "Not ok: 0". This is because negation impacts the exit code of the previous command.
- Dr_Emann 5y agoFor simple cases, I'll do that, but for longer commands, I've taken to doing rc=0 long command || rc = $? if (( rc == 0 ))...
- matvore 5y ago`set -e` has a couple of surprising corner cases and in the details is pretty hard to understand. The documentation in `man bash` for the `set -e` flag is 28 lines in my terminal, and the other flags are 2 or 3 lines. One such corner case is in pipelines. Only the last command in a pipeline can cause the script to terminate. Another corner case is `foo && bar`, often used as an abbreviated `if`, will not exit when `foo` fails. It is not a significant task to just add `|| exit $?` after any command whose failure should cause an abort.
- mgerdts 5y agoMy scripts tend to start with set -euo pipefail This addresses the pipeline case you mention and also notices use of unitialized variables.
- shawnz 5y agoUnfortunately Ubuntu's new bash replacement "dash" doesn't support "-o pipefail" and will error if it's present
- throw0101a 5y agodash is not a bash "replacement". dash is an implementation of the Bourne/POSIX shell. bash, when called as sh, also implements the Bourne shell as well (but badly, because it leaks bash-isms), but when bash is called as bash it is a super-set of Bourne. * https://en.wikipedia.org/wiki/Almquist_shell#dash https://en.wikipedia.org/wiki/Almquist_shell#dash * https://en.wikipedia.org/wiki/Bourne_shell https://en.wikipedia.org/wiki/Bourne_shell * https://en.wikipedia.org/wiki/Comparison_of_command_shells https://en.wikipedia.org/wiki/Comparison_of_command_shells If you want to use super-set functionality adjust your shebang accordingly.
- erdos4d 5y agoBash rarely impresses me but this is a feature I love about it. I write Python normally, I really wish it had this.
- frumiousirc 5y agohttps://www.python.org/dev/peps/pep-0572/ https://www.python.org/dev/peps/pep-0572/
- nijave 5y agotldr; `:=` assignment does this in Python 3.8+ while data := something_that_may_be_false(): # do something with data
- matvore 5y agoThis idea doesn't apply so cleanly in Python. The meaning of this: if x=$(foo); then ... fi is that the stdout of running `foo` replaces the contents of $x and the `if` evaluates the exit code of `foo`. Python doesn't see stdout as some kind of first-class language construct. I also don't think this could ever work on Windows if you expect `foo` to be a simple function that has access to the interpreter state.
- posix_me_less 5y agoYou don't need quotes in res="$(... whatever ...)" This is sufficient: res=$(... whatever ...)
- adolph 5y agoIt depends on what is being returned and how the result is used. In my inexpert experience cat $(find -name <some file with spaces in the name>) will fail.
- jolmg 5y agoThey mean specifically on assignment. Though, when in doubt, quoting never hurts.
- nijave 5y agoIt's specifically an edge case with variable assignment. If it were export val="$( ... )" you'd need the quotes again. You don't need quotes in plain variable assignments and afaik it goes back to how space splitting works in Bash (whether or not you're in a "splitting context" or whatever the docs call it). There's a semi-related set of rules for when * gets expanded and when it's passed through to the underlying command as a literal '*' character
- jolmg 5y ago> If it were `export val="$( ... )"` you'd need the quotes again. No. That's also recognized as an assignment by bash[1]: > PARAMETERS... A variable may be assigned to by a statement of the form `name=[value]`... Word splitting is not performed... Assignment statements may also appear as arguments to the alias, declare, typeset, export, readonly, and local builtin commands (declaration commands). [1] https://www.gnu.org/software/bash/manual/bash.html#Shell-Parameters https://www.gnu.org/software/bash/manual/bash.html#Shell-Par...
- ipnon 5y agoI use this to great affect in Elixir to name magic numbers as one off arguments. Process.sleep(_minute = 1000 * 60)