

Bash Pitfalls - monort
http://mywiki.wooledge.org/BashPitfalls

======
monort
If your bash script is not trivial, and target machines have python, it's
probably better to use sh:

[https://amoffat.github.io/sh/](https://amoffat.github.io/sh/)

~~~
iberieve
First time I've seen this and it looks amazing. Poorly named perhaps, though
it is certainly a nice option to be aware of! Thank you for sharing.

One question - In this py-sh system are there error control options like what
bash provides? e.g.:

    
    
        set -o errexit
        set -o pipefail
        set -o nounset

~~~
monort
These options are set by default - non zero exit code or unknown variable will
raise an exception. You can catch it if you don't want to exit.

------
freddref
Is there a script to check for these pitfalls? (And offer example solutions?)

Has anyone forked bash to remove or fix these pitfalls, while maintaining
maximum "bashness"?

edit: [http://www.shellcheck.net/](http://www.shellcheck.net/)

~~~
e40
_Has anyone forked bash to remove or fix these pitfalls, while maintaining
maximum "bashness"?_

I'm not sure how bash could be changed to prevent user error. Many are not due
to bad bash design.

------
asa400
Li Haoyi just gave a talk at Scala by the Bay entitled "Beyond Bash", which
details the shell scripting environment he's been working on that is hosted in
a reimplementation of the Scala REPL, called Ammonite.

Slides for the talk:
[http://tinyurl.com/beyondbash](http://tinyurl.com/beyondbash) Docs:
[http://lihaoyi.github.io/Ammonite/](http://lihaoyi.github.io/Ammonite/)

I wasn't at the talk, but I downloaded it and have been playing around with
it. It's really fun!

The path operations are all typed (ie, you can't combine relative and absolute
paths in stupid ways), you get all of Scala to operate on files and the
filesystem (if you know Scala, this is pretty huge), and it has a handy
pipelining syntax that is effectively an extension of the shell `|` operator
we all know and love:
[http://lihaoyi.github.io/Ammonite/#Extensions](http://lihaoyi.github.io/Ammonite/#Extensions)

There are other niceties built in as well, like syntax highlighting and pretty
printing, that gave me the impression that the author really cares about the
UX of the software. It's not all academic/pure, in fact it appears to be the
kind of pragmatic, practical thing that I wish Scala was known for. I highly
recommend giving it a shot, especially if you already know Scala. I definitely
will be giving it some time in the coming weeks.

------
vezzy-fnord
See also the Inferno shell: [http://debu.gs/entries/inferno-
part-1-shell](http://debu.gs/entries/inferno-part-1-shell)

I've been playing around with the werc framework, 9base, plan9port and other
Plan 9-derived tooling and have found rc shell to be rather pleasant compared
to Bourne and Korn dialects.

------
jordigh
And people think C++ is hard to do correctly...

~~~
pen2l
Sorry, what do you mean to imply here... that Bash is difficult?

~~~
kzhahou
Is it even up for debate, that bash is hard to get right?

~~~
pdkl95
Bash isn't particularly hard. Unfortunately, it carries a significant amount
of historical baggage that make things very confusing. Many examples and
existing bash scripts usew these older feature which can make learning bash
even more confusing.

A couple examples of what I mean are:

    
    
        # old style command substitution (don't use this)
        echo "`ls *.mp3 | wc -l` MP3 files in $PWD"
        # new style
        echo "$(ls *.mp3 | wc -l) MP3 files in $PWD"
    
        # while it is used in some place, the use of $*
        # to mean "all arguments" is probably always wrong
        for i in $* ; do do_something $i ; done
        # instead, you almost always want "$@"
        # (and always use quotes on variable expansion)
        for i in "$@" ; do do_something "${i}" ; done
    

A _lot_ of really nasty sometimes-incorrect behavior goes away when you use
the modern replacements.

Another big thing that confuses people at first with sh/bash style shell
script is that they treat it like a _regular programming language_. Instead,
realize that most of the magic happens as "expansions" of the command line.
Thinking about bash a something closer to a fancy macro language doing simple
string manipulates can help a lot.

Finally: RTFM. Seriously. Modern versions of bash have a very nice manual.
Cargo-culting pieces of existing scripts may solve an immediate problem, but
it won't teach you the real language nearly as well as simply reading bash(1)
(especially the "EXPANSIONS" section).

~~~
angersock
Is there a good online resource (other than man pages) for those of us who are
exceptionally lazy to learn modern Bash?

~~~
pen2l
greycat's writing are the best thing for bash, in my opinion. The submitted
article is his 'bash pitfalls'... his formal bash guide is here:
[http://mywiki.wooledge.org/BashGuide](http://mywiki.wooledge.org/BashGuide)

greycat is a guy who idles in #bash on freenode and has helped thousands of
people.

~~~
angersock
Thanks!

------
hyperpape
Referencing the other thread on shell scripting currently on the front page
([https://news.ycombinator.com/item?id=10068668](https://news.ycombinator.com/item?id=10068668)),
some of these examples show ways in which bash is actually quite verbose:

    
    
        # POSIX
        for i in *.mp3; do
            [ -e "$i" ] || continue
            some_command "$i"
        done
    
        # HYPOTHETICAL SYNTAX 1
        map some_command *.mp3
    
        # HYPOTHETICAL SYNTAX 2
        some_command *.mp3
    

It's hard to imagine how to create simple syntax for operations like that
while accomodating other syntactic requirements (strings without quotations,
pipelines, etc), but I dream about a shell language that lets me do things
like the above.

~~~
lisivka

      map() {
        local COMMAND="${1:?Argument is required: command to execute, e.g. "echo". Example: \"map echo *\".}"
        shift 1
        local I
        for I in "$@"
        do
            [ -e "$I" ] || continue
            $COMMAND "$I"
        done
      }

~~~
hyperpape
Neat! I'm curious: did you come up with this on the spot, or is this actually
something you use?

~~~
lisivka
I just typed it in window.

------
rando289
We've overwhelmed it. Instead:
[https://web.archive.org/web/20150811151807/http://mywiki.woo...](https://web.archive.org/web/20150811151807/http://mywiki.wooledge.org/BashPitfalls)

------
joshbaptiste
Everything I know of bash I learned from Freenode irc #bash channel who have a
very active bot that always points to this wiki, so many scripts at work I see
the dreaded

    
    
      for i in `ls`; do... 

Which only works due to the fact that we hardly have any files with spaces,
newlines etc..

~~~
frou_dh
There should be a modern filesystem that's catered to nerd tastes and simply
prohibits whitespace in file names.

~~~
JoshTriplett
Alternatively, allow everything in filenames, including whitespace and '/',
but escape them on the filesystem using something like URL escaping (%20,
%2F). No reason the filesystem names have to match the user-friendly names
precisely, as long as a lossless bidirectional conversion exists.

~~~
frou_dh
Neat idea. If I understand correctly, it'd still be fair to say that those
characters wouldn't be allowed in file names since encoding/decoding the
friendly form would be opt-in work each and every userspace program couldn't
be relied upon doing?

~~~
mitchty
Yep, the vfs layer in most unixes won't let you use / or \0 in a filename. And
for good reasons.

------
proactivesvcs
A really helpful guide, particularly for someone just starting out on Linux
such as myself. Hopefully I will not get into bad habits to begin with :-)

Having looked at my scripts I seem to have been pretty cautious already
(Windows batch has already scarred me plenty), but I have shored up a few
minor areas.

