Hacker News new | past | comments | ask | show | jobs | submit login
Show HN: Sensible Bash: An attempt at saner Bash defaults (github.com/mrzool)
173 points by mrzool on March 2, 2016 | hide | past | favorite | 70 comments



I feel like discussion of 'sensible bash defaults' is incomplete without mention of .inputrc. My most "can't live without it" setting for bash is actually an .inputrc setting:

    # From http://www.ukuug.org/events/linux2003/papers/bash_tips/
    # Incremental searching with Up and Down is configured in .inputrc
    "\e[A": history-search-backward
    "\e[B": history-search-forward
... so if I type `ssh <up arrow>` I get the last command that started with `ssh`, not the last command I ran. oh-my-zsh thankfully does this by default as well. But I still use my .inputrc on server boxen where I don't want/need to install a different default shell.

Also worth noting that all of those `bind` commands in OP's script are actually .inputrc lines if you remove the `bind` and surrounding quotes.


This is why these things are so personal. I'd hate this. The up/down bindings are absolutely tied to specific command orders in my head. I need to know that "three commands back" is wherever I was in the sequence of commands that I'm repeating.

If I'm trying to semantically query my history (for "ssh") in this case I'll use the ^R binding to incrementally search the history, or literally grep ~/.history for what I'm looking for.

That's not to say that you're "wrong", just that calling these things "sensible defaults" is sort of missing the point. Nothing about them is sensible for everyone.


If you just press "up" without typing anything first then it works as you expect. I find ^R annoying if I've already started typing the command and then realise I can search the history (although that's probably configurable).

Once you get used to it, the only downside of up/down history search is when you ever use a shell that doesn't do it and you feel lost.


On a standard Ubuntu install: If you press ^R after already typing part of what you're searching for, and then press a key (space, or more of the command you're searching for), then it'll switch to the history-search mode with that value pre-filled in.


I have it as

    "\e[5~": history-search-backward
    "\e[6~": history-search-forward
This allows you to continue using arrows for absolute chronological history then use PageUp and PageDn for history search.

You could also change it to

    "\e[5~": history-substring-search-backward
    "\e[6~": history-substring-search-forward
if you want to search for a string match anywhere in the command rather than at the beginning. It might be 'substr' rather than 'substring' depending on your Bash version.

Obviously the utility of this depends on your keyboard layout and config, as much as it does on personal preferences.

You can see the full list of commands at https://www.gnu.org/software/bash/manual/html_node/Commands-....


Yup, it changes the default behavior slightly but as @omalsa04 mentioned if you just hit <up> without typing anything it will iterate through the whole command history, the same as it would without this binding.


So that is where it actually comes from. I was wondering for some time now. This is one of my favorite features on oh-my-zsh!

It mostly helps when you type complicated command and after half an hour you need to repeat it for some reason. You type the command you used, and hit a arrow up.


I don't think zsh uses the .inputrc file, actually. I've actually never looked into how oh-my-zsh does it.


I have to comment just to say "thanks!" - don't know how I lived this long without knowing this one trick. This just blew my mind!


> Prepend cd to directory names automatically, so you can cd into directories just by typing their name;

I'm pretty sure this is a solution to a problem I don't have today.

> Treat hyphens and underscores as equivalent;

> Automatically correct spelling errors during tab-completion;

I'm terrified to put this in my shell. Then again, it's not all-or-nothing:

> cherry-pick the options you like most ... everything is open for discussion


> > Automatically correct spelling errors during tab-completion; > I'm terrified to put this in my shell.

I had it in once (it's one the options in zsh setup)

Once I was cleaning my home directory:

    rm -rf tec<Tab>
    rm -rf IdeaProjects<Ret>
I'm a bit too quick to press enter after tab. So i deleted the wrong folder.

I do still have it auto correct case. So `doc<Tab> -> Documents`


This is why I delete to trash, even from a terminal. I use the trash-cli package on Debian/Ubuntu.

Also:

    alias rm 'rm -i'


-f overrides -i unfortunately.

(trash-cli sounds good, thanks for the tip)


Prepending cd for directory names would be kind of bad for me.

I tend to create local scripts to initialize project environments using their root directory name. So the automatic prepending would make the action ambiguous as a command.


That's the first thing I disable every time I set up oh-my-zsh on a new box. I think it should not be a default behaviour and should only be enabled if needed.


May be an old-fart prejudice, but my eyes cross to think that 'dash and underline are equivalent' might be a desired thing. Making bash be clever seems like a pile of pitfalls.

To each their own.


That's just for tab completion, not overall. I agree that that would be far from sane.


Yeah, I don't get this one, either. This seems far away from sane.

What's the rationality behind it?


If you have an inconsistent naming convention in your directory structure, this could speed things up/save some memorization.

I'm not a fan of adding features that make poor choices less painful, but I could see using it for a little while as I'm getting to know a non-ideal environment.


I don't have any file or folder name starting with a dash, while underscores are pretty common. Treating dashes and underscores as equivalents saves you one keystroke (i.e. you can type `cd -folder<tab>` instead of `cd _folder<tab>`).


Is this really the reason? One keystroke? That just borders on bloat...

Just put "set -o vi" if you want to remove keystrokes, which this doesn't include.


I've used `set -o vi` for a while but found that emacs-like keybindings are much, much better for inline editing.

And yes, even one keystroke saved matters.


Oh no, I totally understand - typing this on a happy hacker keyboard ;).

I'm just somewhat of a cynic, since my workflow tends to fall on its face when I'm on a server that uses special snowflake rc files. Furthering that cynicism.. these days I'm just using plain /bin/sh for any shell scripts just because of the amazing level of bloat that exists within bash.


You mean it saves you one edit in the case where you've mistyped the directory name? Otherwise it seems like no keystrokes are saved among the two you show.

EDIT: groan, ok, you win. Net -1 keystroke for most keyboards.


The shift press doesn't count as keystroke? :)


Most keyboards only emit an underscore while a shift button is depressed.


It would be nice if bash had a more discoverable version of the 'help' command for new users.

Right now, someone (say, a bright 12-year-old) who opens up a terminal for the very first time and types "help" is greeted with this lovely mess:

     compgen [-abcdefgjksuv] [-o option]  >  return [n]
     complete [-abcdefgjksuv] [-pr] [-DE] >  select NAME [in WORDS ... ;] do COMM>
     compopt [-o|+o option] [-DE] [name ..>  set [-abefhkmnptuvxBCHP] [-o option->
     continue [n]                            shift [n]
     coproc [NAME] command [redirections]    shopt [-pqsu] [-o] [optname ...]
     declare [-aAfFgilnrtux] [-p] [name[=v>  source filename [arguments]
     dirs [-clpv] [+N] [-N]                  suspend [-f]
     disown [-h] [-ar] [jobspec ...]         test [expr]
     echo [-neE] [arg ...]                   time [-p] pipeline
     enable [-a] [-dnps] [-f filename] [na>  times
     eval [arg ...]                          trap [-lp] [[arg] signal_spec ...]
     exec [-cl] [-a name] [command [argume>  true
     exit [n]                                type [-afptP] name [name ...]
     export [-fn] [name[=value] ...] or ex>  typeset [-aAfFgilrtux] [-p] name[=va>
     false                                   ulimit [-SHabcdefilmnpqrstuvxT] [lim>
     fc [-e ename] [-lnr] [first] [last] o>  umask [-p] [-S] [mode]
     fg [job_spec]                           unalias [-a] name [name ...]
     for NAME [in WORDS ... ] ; do COMMAND>  unset [-f] [-v] [-n] [name ...]
     for (( exp1; exp2; exp3 )); do COMMAN>  until COMMANDS; do COMMANDS; done
     function name { COMMANDS ; } or name >  variables - Names and meanings of so>
     getopts optstring name [arg]            wait [-n] [id ...]
     hash [-lr] [-p pathname] [-dt] [name >  while COMMANDS; do COMMANDS; done
     help [-dms] [pattern ...]               { COMMANDS ; }
    user@host:~$


Most of these are included in /usr/bin/fish :)


Came here to say this! Got tired of the little quirks, and never looked back.


Yep, for interactive use, Fish is fantastic. Bash is still a better choice for most shell scripting though.

Also recommend using something like Percol or fzf for filter-as-you-type command history.


Hey guys! If you are interested in "different" shells and REPLs I suggest you check out Ammonite-REPL / Ammonite-Shell -http://www.lihaoyi.com/Ammonite/ It's written in Scala and well. It looks ql. :)


This looks pretty sane overall. If you try this, be aware that it changes directory navigation, i.e. "Prepend cd to directory names automatically, so you can cd into directories just by typing their name", "Automatically correct spelling errors in arguments supplied to cd". I've tried these things and they drove me insane, so they're not good defaults for everyone.


I like it. It is not a complete revamp of bash to something else (the zsh-comment below is not fitting), it just enables some comfort features bash readily supports. One can argue about the case-insensitivity, but I see no other option that could be controversial. Just a small collection of sane defaults.


In other words: making bash more like zsh


Speaking of zsh, here's my zshrc file: https://github.com/jleclanche/dotfiles

Sane defaults, useful aliases, no dependencies and easy to expand upon. I made it mostly to give either a sensible default rc file to extend, or just a general high quality prompt.


I'm a big fan of zsh. It doesn't seem to be solely focused on just "doing what you mean", which some of these tweaks seem aimed at; it seems aimed more at correctness. e.g., it would allow you to pass * .txt into a command if * .txt doesn't match anything. Bash will just assume I mean literally '* .txt' so you get different behaviour depending on whether or not there's a wildcard match. zsh has taught me the good habit of always using \* when I don't explicitly want shell expansion.

edit: spaces add after asterisks to avoid formatting weirdness


Heh, fish shell contributors had a long discussion of failed wildcard expansions at https://github.com/fish-shell/fish-shell/issues/2394 . We all pretty much agreed that bash's passglob behavior is really bad.

zsh's default is much better. Though to be fair, bash-style passglob behavior can be enabled in zsh by setting NO_NOMATCH. zsh is still zsh, after all.

There's still plenty more wildcard nuttiness. A favorite is to execute a bare *: zsh will try to execute the first file, passing the other files as arguments!


Came here to say the exact same thing.

Get zsh today. Give it a spin.


I hear a lot about it -- could you give me your favorite 2-3 features that make you prefer it over bash?


Three things I use regularly, even in scripts:

1) Glob qualifiers:

  ls *(.)
will only match files, not directories, symlinks etc.

  ls **/*.jpg(.L-3000000on[1,5]) **/*.png(N)
will match .jpg files in the current and all subdirectories, which are:

  . files
  L-3000000 with "length" (size) under 3MB
  on ordering them numerically
  [1,5] and only the first five
plus any
.png files, if they exist (N = it won't fail if they don't).

I have some scripts with expressions this complicated, but day-to-day it's useful for finding empty files (L0) or excluding directories. "man zshexpn" and search "Glob Qualifiers".

2) Glob operators:

  ls DSC<100-200>.JPG
matches files with names between DSC100.JPG to DSC200.JPG.

  setopt extended_glob
  ls *.java~*Test*
matches all .java files, except those matching Test*. Same man page, search "Glob Operators".

3) Lazy for-loops (single command doesn't require do-done), and taking two variables:

  echo "A 1" > tempfile
  for i j in $(<tempfile); echo $i $j
4) Parameter expansion.

  > x=EXAMPLE.jpg
  > echo $x:r.png ${x:r:l}.png
  EXAMPLE.png example.png
Interactively, shell history seems to be nicer, I use a couple of global aliases (alias -g L='| less'), and the completion system completes everything you can possibly think of, like usernames, hostnames, remote filenames, HTTP URLs, processes (for kill)...


Great list - I didn't know about glob operators. That glob negation is going to be very handy.

Re number 2: That works in plain old Bash too:

    ls DSC{100..200}.jpg


That's not quite the same. Zsh's <X-Y> is file globbing, only files that exist are returned. {X-Y} is parameter expansion without regard to files:

  > touch 1 3
  > ls <1-3>
  1 3
  > ls {1..3}
  cannot access 2: no such file or directory
  1 3
Nevertheless, that's a useful and more powerful alternative to seq that I didn't know about.


I am using zsh + prezto. https://github.com/sorin-ionescu/prezto

Tab completion is extremely powerful. Even serious spelling mistakes are forgiven.

The prompt is Git-aware and this provides a lot of helpful information which saves me from having to query the git cli.

There are also plugins available for completion of other programs such as rake


Or fish, which is basically zsh with sane defaults.


And a completely incompatible non-POSIX syntax, which sounds great in theory but isn't nearly enough better to justify the departure IMHO.


Why does compatibility matter? If you have a script written for sh or bash it will just run using that interpreter.


There are http://www.commandlinefu.com and the like. I copy and paste stuff from the web into the shell occasionally, I'm sure everyone does. I do use fish, but it is annoying sometimes, it's missing the most basic stuff like backticks. So much so that I think I'm going to stop.


I'm sure you know this, but it's not missing that feature. It's just that the syntax is different.


For starters, any utilities based on sourcing won't work, such as nvm/rbenv/etc.

That's technically true for zsh in cases, but it's far easier to port or add compatibility for zsh than fish.


You can use bass which solves this problem without porting the script to fish: https://github.com/edc/bass


Or bash, since it will be installed everywhere, you don't have to get it, and most scripts you'll find online will work on it.


This is a non-issue. The `!#/bin/bash` at the top of scripts will tell the interpreter which shell to run. And any one liner I've found online for bash has also worked on zsh (for me), but even if it didn't: `bash -c ls`


Yes exactly.

Today I learned people actually write / use scripts without specifying the interpreter at the top of the script. This is like writing a Ruby script and expecting it to work when calling it with the Python interpreter.


Yeah, it's actually not installed everywhere (e.g. FreeBSD).


Anywhere that matters. Besides, still more places than fish or zsh.


Minus it being bash-compliant. Deal breaker for most.


These history settings should clearly be the real defaults. I wonder how difficult it would be to get them changed upstream?

Upstreaming would also allow improvements, for example PROMPT_COMMAND='history -a' doesn't update history until the next prompt is displayed. If you close your terminal while a program is running then the last command entered is not saved.


Some great ideas here--a lot of them solve problems I've personally had with bash over the past 15 years--but plenty of these feel like solutions in search of problems.

I'm tempted to post my own commentary but I'd probably make the same mistakes. What would be really fantastic is a usability study of bash to see what the common pain points are.


Does anyone know if bashscript could be updated to allow spaces between assignment? I can't see how it would break existing programs (but I'm sure there's a catch) and scripts would be much cleaner (and less of an headache).


A breaking example might be trying to find lines containing an "=" in a file:

    grep = my_file
Also a problem is the syntax for running a program with environment assignments that apply only to the program:

    env1=foo env2=bar env3= my_program
Note that under POSIX rules, "env3" here is assigned a zero-length string. Making these sorts of assignments work with spaces around the equal signs would open up a can of worms.


Minor nitpick: why is the session history larger than the maximum file size?

It always annoyed me that HISTTIMEFORMAT is not set by default. Without it, log entries are simply not timestamped at all.

The readline tweaks were news to me, thanks.


HISTFILESIZE is kind of a misnomer. It's the maximum number of lines in HISTFILE, where HISTSIZE is the maximum number of commands. If you have HISTTIMEFORMAT set, then each command gets at least 2 lines, plus the possibility of multi-line commands.


a lot of this stuff is present in cygwin but by default commented out. Something what made me more productive is a cd_func:

out of .bashrc:

function cd_func

This function defines a 'cd' replacement function capable of keeping,

displaying and accessing history of visited directories, up to 10 entries.

To use it, uncomment it, source this file and try 'cd --'.

acd_func 1.0.5, 10-nov-2004

Petar Marinov, http:/geocities.com/h2428, this is public domain


I've got `alias cd=pushd` in my .bashrc. It keeps track of visited directories as a stack (arbitrarily deep).


The site redirects to something unrelated, is your link broken?


Sensible Bash... like Fish or Z Shell?


fish is not even a sh, much less a bash.


That's true, but everytime I say the words z shell, someone always says "Fish!", so I figured I'd pre-empt them for once. Go figure it would backfire on me. LOL.


Or just use fish. Why is this even a conversation in 2016?


I thought 'sensible bash' was called zsh.

Just kidding, but not really, zsh is great and basically compatible with Bash, check it out.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: