Cover image credit: Fonts.com
Bash has lots of different kinds of brackets. Like, many much lots. It adds meaning to doubling up different brack...
Some comments have been hidden by the post's author - find out more
For further actions, you may consider blocking this person and/or reporting abuse
You missed one of the really great BASHisms:
<( COMMAND )
. Basically, run command in a subshell, then return it's output through a file-descriptor. Meaning that you can do things like:Another great one is
VAR=($( COMMAND ))
...which takes the output fromCOMMAND
and creates an array-variable from it.Also useful is
$( COMMAND )$?
for when you care about how a command exited but not its output (e.g., you want to test to see if grep found a string in a file,[[ $( grep -q PATTERN FILE )$? ]]
.These are great! When I get in front of a keyboard, I’ll add them to the list! Thanks for sharing. What are the most common file-expecting commands you use the sub shell redirect with?
AWS CLI for (particularly for CloudFormation) seems to not quite process STDIN for the
--parameters
flag as one might expect. Usually have to do something like what I put in my article when I'm doing stack-iterations as part of a quick test-change-test cycle (where "quick" is relative to things like RDS - there's frequently not enough time to wait for one launch to delete before moving on to the next launch).Thanks again for the tips! I just wanted to let you know that I updated the article with your suggestions. 😀
the
$( COMMAND )$?
trick is a very bad one. Please don't advertise it, it basically NEVER works as you intend it to.Not sure what your experience is, but mine is that it works pretty much exactly as one would reasonably expect. It's functionally equivalent to executing something like:
But without any shell-linters bitching about using an inefficient execution-form.
That said, you need to be familiar with what the subshelled-command's likely outputs are going to be. Which is to say:
/dev/null
. Failing to suppress output will tend to cause evaluation-logic to evaluate as a string of<COMMAND_OUTPUT><COMMAND_EXITCODE>
rather than an integer of<COMMAND_EXITCODE>
.-eq 0
or-ne 0
output, you need to be familiar enough with the given command's possible exit-codes to set up the appropriate handlers you might want/need.You totally misunderstand what
[[ $? ]]
actually does. It is actually equivalent to[[ -n $? ]]
, with-n
being the operator for non-empty string test. Since$?
always contains an exit status of the last command, it will always be a non empty string, so[[ $? ]]
will always return 0 and evaluate to true in if context.Please read up on how
if
works in bash and stop screwing up noobs with worthless code.Ok, so, I shortcut on my prior statement. The response was basically, "here's something you can start your Googling with," not, "here's a whole freaking book on the topic." But, congratulations, rage-boy, in your quest to service "noobs" (great to throw that term out there, btw: really shows your head-space and overall level of respect for your community-members), you've extended this thread beyond the scope of a simple Google-starter. And for all of that belaboring, the point still stands that there's many useful, reliable ways to use the
$( command )$?
construct. Clearly your opinion differs. Clearly you've been badly snakebitten (or otherwise simply have an axe to grind). However, like with any useful tool or construct, just because there are ways that it can fail within a given context, it's still useful when correctly used in the right contexts.Hi guys! Just FYI, I’ve muted and moderated both of your comments in this particular thread. Let’s try to keep it positive and helpful for the people who need this info next time. I’d appreciate it if you’d take any further posturing/bickering into dms and out of my comments. Thanks!
You have not demonstrated any useful way of using the
$( command )$?
construct. Your original one is dangerous and misleading. I'm asking you, please stop screwing over noobs - bash can be very unforgiving and can do a lot of damage when used incorrectly.No worries. After my final reply, I did a "who is this guy" click on his profile noticed that he'd apparently created an account just to piss on the thread (created the day he started thread-crapping and only activity shown on profile is the thread-crapping). So, had no further intent to respond.
Yes, I'm new here, but I'm not new. I write a LOT of bash on my job, so when I see these dangerous "recommendations", it makes me cringe.
And you, my friend, should reign in your prideful graphomania and actually TEST your code before you post it.
A couple of things about HEREDOC bits:
stdin
for a command. The way you've used it in your first example for assignment to a variable doesn't work, at least for me and my bash 4.4.19.Hm. I’ll take a look at that. Thanks! I was doing all of my local testing in Zsh, and forgot that there might be differences.
I updated the post with your help and extra info! Thanks again!
This is a really great overview. Probably the clearest thing I've ever read about substitutions and their ilk :)
One thing I might add to it would be just to flag which bits are POSIX. Like where you have the single- vs. double-square-brackets rule of thumb, you say that if you need to use
test
or[
you'll know it - well the main reason you'd know it is if you were wanting to write a script which might be portable, i.e. work in a foreign shell.Thanks! I’ll put something in there about that. I tried to avoid it, because I found during my research that articles that constantly focused on POSIX really distracted from the rest of the info. But you are right that it’s probably good to at least mention that it is a thing.
Thanks for the feedback!
Small fix for -
echo ${url%%/*}
It actually prints -
https:
Ah! You're totally right. Let me get that fixed. Thanks!
Great summary.
I've written a ton of bash.
My rule for writing bash is to write as little bash as possible.
Anything with any sort of even mildly complex logic, use a modern scripting language - Ruby (my preference) or Python.
I've also written github.com/thewoolleyman/process_h... which makes dealing with subprocesses much nicer in Ruby.
My experience has been the exact opposite. Bash, when properly learned, is very powerful and, what's more important, SELF SUFFICIENT.
Ruby and Python either make for very long scripts to accomplish even the basic tasks or pull in a gillion libraries as dependencies, which turns it into a sysadmin nightmare. BTW, the process helper library you linked to is a classic example of that.
My personal preferences for scripting: bash when possible, Perl when necessary, Python if you're adventurous and NEVER Ruby.
Cobol forever (:-}
Nice overview. The curly brackets can be also used to encapsulate function body.
And to concatenate output of several processes:
Thanks for this valuable article. So many of us use bash scripting without ever properly learning it.
I want to point out that, in a Boolean context, the
(( ))
is an evaluation of the contained expression, not the exit code of the double parens itself. For example:Even though the exit code of
(( 2 - 2 ))
is 1 (true), this outputsdie
since the evaluation of the expression 2 - 2 is 0.Note that
(( (( 2 - 2 )) ))
would, however, be false, since the evaluation of(( 2 - 2 ))
is its exit code.Love this post. Bash has remained something I've been content to stumble around with instead of learning, but this got me intrigued.
Heh... There's so much I can do with BASH that, when under a time-crunch, it's hard for me to justify figuring out "how do I accomplish 'X' in <INSERT_LANGUAGE_OR_DSL>" ...when I can just bang it out in BASH in like two seconds
Yeah, I logically know all of this but have still maintained vast ignorance.
Basically until I wrote this, I just guessed and then tried another set of brackets when one didn’t work. I do that with a lot of stuff in Bash, so I’m going back to fill in all the gaps.
This is great! I use
bash
a ton but wasn't aware of several of these. Really useful.Thanks! Glad it was able to help you out!
It's nice to show off some tricks, but the amount of bad and downright DANGEROUS practices in this article is just too much. Really, sometimes it's just best to READ THE F-CKING MAN PAGE!!!
Hi there! I found the man pages dense and opaque at times, which is why I put this article together. Are there any especially dangerous practices that you’d like to point out?
It's true that bash man page is a very dry reading, but once you get the basics figured out, it is definitely the best source. That's why it's important to use the correct terminology in your article - it will allow people to quickly find the necessary subjects in the man page.
Now, for the mistakes in the article.
"$( Dollar Single Parentheses )" is actually called "Command substition"
"$( Dollar Single Parentheses Dollar Q )$?" is an absoultely HORRIBLE trick, NEVER EVER EVER do this! Oh, and btw, this doesn't actually work, because, the few times that it won't spew out an error, it will always evaluate to true.
"$(( Dollar Double Parentheses ))" is actually called "Arithmetic expansion" with the expression within undergoing "Arithmetic evaluation". An interesting fact: double paratheses is not the only place arithmetic evaluation occurs: it also occurs inside array subscripts, which can be a pretty surprising thing sometimes.
[ Single Square Brackets ]. This deserves a special caution: unless you require backward compatibility with classic sh, you should NEVER use it. It's error-prone, requires gillion of quotes to make reliable and spawns a separate process for no good reason. Also, "[[" has more features.
"{ Single Curly Braces }" is called "brace expansion".
"${dollar braces}" is called "parameter expansion".
Yep, I’m aware of what these are all called. I wrote it this way because the target audience most likely wouldn’t, and I wanted to make it an accessible visual guide. That being said, your other notes are helpful, and, when I get a chance, I’ll do my best to add the warnings and caveats where they’re needed. Thanks for taking the time to walk me through your experiences!
Great article! Thanks for writing it!
Thanks! Glad you liked it!
A useful list. Thanks for writing this up :)
There is also triple < here doc that provides a string as stdin input, e.g.
cat <<<"this string"
Cool! Does this give you any benefits over “echo some string | cat”? I guess it saves cat from being run in a sub shell, maybe?
I once renegotiated my salary when the CTO asked "But how did you passed standard input to the application?". Got a 30%. lolz ;..;
I think you made a mistake just under "Using a default value if the variable isn't defined.". Check the "," after "Hello"
Otherwise really great stuff! Thanks!