set -o errexit # or set -e if you prefer
Then you probably also want: set -o pipefail
Otherwise, it only checks that the last command succeeds, so something like: ls *.ssjkfle | wc -l
will actually continue as success despite the "ls" failing. DEST_PATH=/opt/$VENDOR/$DEST_DIRNAME
#remove destination
VERSION=`cat "$DEST_PATH/bin/.version"`
if rm -fr "$DEST_PATH"; then
echo "INFO: $APP_NAME (ver.$VERSION) has been uninstalled successfully."
...Scary to see a big company like Samsung release code this bad.
function cleanup {
...
}
trap cleanup EXIT
See more here: http://linux.die.net/Bash-Beginners-Guide/sect_12_02.html trap "{ echo; exit 1; }" INT #!/usr/bin/env bash
Instead of: #!/bin/bash
This makes the script more portable as you don't rely on bash (or any executable) to be in /bin.The other thing is, if you're targeting obscure systems, wouldn't it be just as likely that there wouldn't be a /usr/bin/env, /usr/ might not be mounted, or that bash might not be installed at all?
I suppose what I'm asking is: for practical purposes, is the lowest common denominator /usr/bin/env or is it /bin/sh?
your script is longer than a few hundred lines of code
you need data structures beyond simple arrays
you have a hard time working around quoting issues
you do a lot of string manipulation
you do not have much need for invoking other programs or pipe-lining them
you worry about performance
It's not a sign you shouldn't be using bash. It's a sign you shouldn't be using ONLY bash.People who insist on rewriting an ENTIRE program in Python, Perl, or Ruby fail to understand the Unix philosophy (this is a real misunderstanding I've encountered in my work, not a straw man).
You can just write the complex part in another language, but keep the rest in bash. In other words, main() stays in bash. Python et. al. is used as just another process. bash is a language for coordinating processes.
You don't want a 2000 line bash script. But it can be worse to have a 5,000 line Python script that shells out to tons of other utilities, or awkwardly and verbosely reimplements 'xargs -P' or 'sed -i'. Often you can do the same job with 500 lines of bash and 500 lines of Python (or C), and that is the ideal solution.
Python and bash are pretty radically different languages, and they are not interchangeable (the advices "just rewrite in Python" seems to imply they are). You can use each one for the things they are good at.
Then, when bash isn't enough, I write the difficult parts in Python as standalone utilities and use those from bash. Consequently, I already have a library of little tools in my ~/bin that are generic enough so that I can just use them from bash right away. But every now and then I need to write a special tool that my bash program can use.
Most of my programs "finish" after this stage and they can live for years as a completed bash script calling helper programs. They do their job and there's nothing to add. As long as the program continues to "live on" I just make changes to the bash script or the helper programs.
If at some point I need features that bash can't offer, such as more speed or more complex data processing, I begin to consider rewriting my program in one language. But this is only after the first revision of the program is already complete and I know exactly what to do when porting.
Thus, porting the program is initially only about rewriting in another language, not developing it further at the same time. The first months or years of using the bash script turn into a prototyping stage, and this allows my rewrite be concise, well-thought and clear-partitioned.
In other words, the rewrite becomes a new stable baseline that contains only the best of my bash prototyping, and only solves a perfectly scoped problem. As soon as the port becomes a drop-in replacement for my original bash program I switch to using it and start a development branch to support the new features, if any. Usually the newly gained speed is enough already.
And usually the rewrite happens using C or Scheme. There's rarely enough point in rewriting the script in Python which may already be used to some extent in utility programs that the script calls. At that point I don't want to extend the program but lift it onto a new platform to cut some old baggage that's not needed anymore. There's a certain kind of joy rewriting in a new language something that you know completely before hand. This allows you to only work with the language since the underlying problem is already solved.
Coincidentally, this new C or Scheme program might then, one day in the future, be called from a new bash script...
Python is my choice when I need a bit of logic in my scripts, but it's just a personal choice of course. A good addition for simplifying execution is the "sh" library that lets you call system commands easily as if they were python functions: http://amoffat.github.io/sh/
So very often people lose track of when to use what tools. (Although admittedly, so very often people are forced into some tools by external constraints.)
http://www.etalabs.net/sh_tricks.html
Shell scripts are great, I use and write them every day (and quite advanced ones, too). But it's very hard to make a shell script robust.
Unfortunately it's hard to find a replacement that is stable and installed everywhere. Perl is pretty close. And python too, if you are careful about making your script compatible with all the different versions.
complete -r
disables Bash 'smart tab completion', which in theory is a great idea ( use tab to complete arguments or only list files applicable to the program ) but which never seems to work properly for me.Disabling it saves a lot of frustrated tab-banging.
cat <<REQUEST_BODY |
{
"from" : 0,
"size" : 40
}
REQUEST_BODY
curl http://localhost -d @-
It allows to pass heredoc text to standard input of next command. curl http://localhost -d @- <<REQUEST_BODY
{
"from" : 0,
"size" : 40
}
REQUEST_BODY cat <<REQUEST_BODY | curl http://localhost -d @-
{
"from" : 0,
"size" : 40
}
REQUEST_BODYI use fish as my main shell and its slightly better, but just testing things on variables can be a huge mess.
Do you have any recommendations to make fish play better with shell scripts intended for bash?
shok proposes that the solution to awkward-shell-syntax is to syntactically separate the programming language from program-invocation.
See http://www.tldp.org/LDP/Bash-Beginners-Guide/html/sect_02_03...
One of my main complaints with bash.. the file is evaluated in order - you can't call a function on the line before it's declared.
This fails:
#!/usr/bin/env bash
bar='bar'
foofunction
foofunction(){
echo 'foo'
echo $bar
}
Basically you have to write your entire script in reverse, and i'm unaware of a good way to get around it.You probably want the interpreter to be smart: "Am I loading a script from a file, or am I receiving instructions interactively on the command-line?" But now there's two modes of execution, and code in a bash script won't work if you type it into the prompt yourself. That's a bit uncomfortable.
#! /usr/bin/env bash
set -e -u
bar='bar'
main() {
foofunction
}
foofunction() {
echo 'foo'
echo $bar
}
main "$@"Project page: http://www.zx2c4.com/projects/password-store/ Source: http://git.zx2c4.com/password-store/tree/src/password-store....
Googling bashlint, shlint turns up some discussion (bash -n, ksh -n, zsh -n, some github projects), but I doubt they cover this article's specifics - though most (all?) of it could be automatically checked. I think some could be automatically added (e.g. set -o nounset) - perhaps a bash-subset (or coffeescript-style language) possible...
:SyntasticInfo
lists 'sh' as the linting program for bash scripts.I can see universal application of ${} being advantageous in avoiding accidental "$foo_bar where you meant ${foo}_bar" situations, and ${} makes it clearer that you're referencing a variable. The only cost would seem to be more typing.
Better is subjective... About half my scripts depend on those features. For default arguments, and fail early.
COULD_BE_SET=${COULD_BE_SET:altvalue} set -e
and not: set -o err
etc.