Ask HN: Best things in your bash_profile/aliases?

345 points by yesenadam ↗ HN
What are the best, most useful aliases or functions etc for bash that you've collected or written? Or bash scripts. Please give explanation/usage if necessary. Ones you actually use often. Thanks!

287 comments

[ 2.8 ms ] story [ 273 ms ] thread
Since I'm working in git repos the majority of the time, I type these hundreds of times per day:

    alias s="git status"
    alias d="git diff"
I prefer looking at this stuff in a terminal rather than an IDE. Reducing them to a single character just feels so good. Note: not a git alias like "git s", literally just the single character "s" in Bash itself.

And coming in third:

    alias ..="cd .."
I have the same exact aliases but they check for a Git or SVN repo and then use the right tool accordingly.

I also have `..`, but also `...` which maps to `cd ../..`

I also have '..' and '...', AND '....' heheh. I use that last one a lot

  alias ..="cd .."
If you use zsh, typing a directory name changes to it automatically. It’s almost always a very delightful thing.
I like

  alias h="cd -"
to go to the last directory I was in. Inspired by how you move the cursor to the left in vim.
Bash has this as well

  set autocd
that would be rather

    shopt -s autocd
Yeppity.

> autocd If set, a command name that is the name of a directory is executed as if it were the argument to the cd command. This option is only used by interactive shells

Whoops, sorry. That's right.
side note: i started to get into zsh and then serverless. zsh was balking at finding node.js/yarn stuff and running commands. I had to revert back to bash
You should try Diff2HTML - https://diff2html.xyz/ install the cli with `npm install -g diff2html-cli`

    alias diff='diff2html -s side'
One/two char git aliases took me to the next level:

    alias ci='git commit'
    alias co='git checkout'
    alias d='git diff'
    alias dc='git diff --cached'
    alias l='git log --oneline --graph --all --decorate'
    alias ru='git remote update'
    alias s='git status'
I have one that takes an optional numeric arg for the number of times to repeat. Surprisingly useful, but I still keep meaning to get round to making a nicer interactive one with fzf or something.

   $ alias ..
    ..=up
    $ type -f up
    up () {
        local tmp_path=''
        for i in $(seq 1 ${1:-1})
        do
            tmp_path+='../'
        done
        cd "$tmp_path"
    }
My most commonly used thing in my profile is this combination git tag, push, and npm publish using the current version in my package.json file:

   	np() {
		PACKAGE_VERSION=$(node -p -e "require('./package.json').version")
		SHORTVER="v$PACKAGE_VERSION"
		LONGVER="Version $PACKAGE_VERSION"
		echo $LONGVER
		git tag -a "$SHORTVER" -m "$LONGVER" && git push --tags && npm publish
	}

    node -p "require('./package.json').version"
no need for -e, or also just:

    jq -r .version package.json

  alias search='set -f;search';search() { find . ${2:+-name "$2"} -type f -print0 | xargs -0 grep --color=auto "$1"; }

Recursively searches the files in the current directory for string matches. You can optionally provide a second glob argument to restrict what files to search. This is basically a dumber version of ack-grep, but I wrote it before I learned about ack.

  alias ll='ls -l'
"alias ll='ls -al'" is the first thing I type every time I ssh into any system that isn't mine.
(comment deleted)
I have 'usb' aliased as my main USB's directory.

Making 'cd' more explicit:

  cd ( ) {
  builtin cd "$@"
  echo "$OLDPWD -> $PWD"
  }
Up directory

  alias ..='cd ../' 
This one I use to find bits in my favourite books/essays, which I have in folders in /books, under e.g. /books/Shakespeare/Macbeth, then search with 'book to be or' etc.

  book () {
  # Find occurrences of words in texts. Put folders in ~/books, or soft links (ln -s) 
  # Displays only complete words found, not parts of words.
  GREP_COLOR=red
  grep -r -n -i --word-regexp --color -C 1 "$*" ./books/
  echo `grep -r -i "$*" ./books/ | wc -l` occurrences of \"$*\" found, `grep -i -r --word-regexp "$*" ./books/ | wc -l` as a whole word.
  }
'dlm' downloads mp4 file from URL in clipboard

  #!/bin/bash
  #dlm - download mp4 from clipboard URL. usage: 'dlm myfile' downloads myfile.mp4
  #with resume after 5 secs until finish.
  fname=$(pbpaste)
  echo "Download $fname as $1.mp4 : "
  until curl -C - -kLo $1.mp4 "$fname"
  do
        sleep 5
  done
alias lcoate=locate

alias ll="ls -l"

alias ..="cd .."

alias ...="cd ../.."

alias mdd='mkdir $(date -I)'

alias cdd='cd $(date -I)'

alias xt="xtermset -T "

alias dtail="dmesg|tail"

    alias cdl="cd (ls -ltr1 | grep '^d' | tail -1 | awk '{print $9}');"
cd to last directory by date.

  mcd(){ mkdir "$1"; cd "$1"; }
  alias vd='vim -d' # vimdiff
  alias grep=egrep
Zsh function to create a directory and cd into it in one go:

  function mkcd() { mkdir -p "$@" && eval cd "\"\$$#\""; }
The one I use most often is undoubtedly `g` for `git`. Then I have many aliases inside Git for all kinds of regularly done things, quite a few lifted from Mercurial and then extended; some just shorter names (ci = commit, glog = log --graph) and some more advanced like these three:

  fixup = "!f() { TARGET=\"$(git rev-parse \"$1\")\"; shift; git commit --fixup=\"$TARGET\" \"$@\" && git -c core.editor=true rebase --interactive --autostash --autosquash \"$TARGET^\"; }; f"
  in  = "!f() { case \"x$1\" in x-*|x) branch=;; *) branch=\"$1\"; shift;; esac; git glog \"$branch..$branch@{upstream}\" \"$@\"; }; f"
  out = "!f() { case \"x$1\" in x-*|x) branch=;; *) branch=\"$1\"; shift;; esac; git glog \"$branch@{upstream}..$branch\" \"$@\"; }; f"
My most recent addition to my shell profile (a couple of weeks ago) enhances ts from the moreutils package. ts provides timestamps (relative to process execution or absolute) at the start of each line of stdout. My enhancement makes it cover stderr as well, so that stdout lines get a green timestamp and stderr lines get a red timestamp. Due to how interlacing works, and the fact that I commonly pair this with concurrency in the form of `make -j8`, it’s not perfect (can end up with a line possessing both green and red timestamps, and then none on the next line), but it’s still really helpful. I just need to get round to adding something to it to make programs still colour their own output. `script` can do that, I think.

It’s currently this shell function:

  ts-fancy() {
    ((("$@" 2>&1 1>&3 3>&-) | ts -s "^[[91m[%H:%M:%.S]^[[m") 3>&1 1>&2) | ts -s "^[[92m[%H:%M:%.S]^[[m"
  }
(Each ^[ shown there is a literal ␛.) ts-fancy was the temporary name I gave it at first, haven’t renamed it to anything more permanent yet, so it’ll probably stay that way forever.

Usage, `ts-fancy make -j8 --keep-going` and things like that. It’s proving really helpful when figuring out what’s slow in something that verbosely logs, and also in highlighting error output mixed in with normal output.

This exercise made me wish for a smarter terminal that kept track of timestamps and whether output went to stdout or stderr itself (at the character level, even, to resolve the problems of this line-based approach). I shouldn’t have to do this, and you know how often you think after the fact that it’d be nice to have timestamps?

Very nice! Thanks you for sharing this!

It really seems very useful to have timestamps for every line of output.

I have tried to approximate this in the past, by setting my bash prompt to be:

    export PS1='\[\e[00;37m\]$?\[\e[0m\]\[\033[1;32m\]\[\033[1;32m\][\t]\[\033[1;41m\]\u\[\033[1;41m\]@\[\033[1;41m\]\h:\[\033[0m\] \[\033[1;32m\]\w\[\033[0m\] \$ '
... which shows the previous command exit code, the current time (with second resolution) and the ordinary stuff like username host and working dir, so it looks somewhat like:

    0[21:20:18]delian66@mypc: ~/Work $ slow_command
    ....
    0[21:22:19]delian66@mypc: ~/Work $
I contemplated that for a while a few years back, but decided against it for several reasons:

① I didn’t like an overly-long prompt. On my own machine I already exclude username and hostname, so it’s just `~/Work$ `. (Sometimes PWD gets a bit long, but generally not too much.)

② It was fairly rare that I actually cared about such a timestamp in any way—if I did, it was normally about time taken, and so I could pipe the program through `time` (this was before I discovered `ts` when figuring “someone must have made a program to do this” a couple of weeks ago when preparing to optimise a build process at work), or execute `date; …; date`.

③ Such a timestamp is only written when the prompt is output, rather than when the program is executed (there may be a sane way to have it work otherwise—I didn’t investigate in any way thoroughly—but my initial thoughts suggested any hacky attempt to move the cursor after the fact would interact badly with commands that cause the command line to span multiple lines). This made it useless for well over 90% of the cases when I might have benefited from it (retroactive timing of the duration of a command, rather than at what o’clock I… probably did something).

I return to the desire for a very mildly intelligent terminal that notes when characters were written to screen when not in raw mode, for optional viewing.

I've just made this [0], based on your idea (differently colored timestamps for both stderr and stdout).

It just spawns the actual command in a sub process, but may be the output may be more ordered than that of a pipe of ts commands.

[0] https://github.com/spytheman/gostamp

I work at a company with micro services so we have a lot of repos. We use nodejs and in some cases use different node versions among different services. Having the prompt show what branch I'm in and what node version I'm on is quite helpful:

  PS1="\[\033[01;31m\]Node \`node --version\`: \[\033[01;32m\]\`pwd\` \`git status -sb 2> /dev/null | head -n 1\`\n\[\033[01;34m\]\`date +'%a, %B %-d, %-l:%M:%S%P'\` \[\033[01;31m\]\$ \[\033[00m\]"

Pruning branches both remote and locally are also very helpful to do in these alias/functions:

  function gitpruneremote {
    git remote prune origin
  }
  
  function gitprunelocal {
    git branch -r | awk '{print $1}' | egrep -v -f /dev/fd/0 <(git branch -vv | grep origin) | awk '{print $1}' | xargs git branch -D
  }

Git pulling in every repo:

  function gitpullall {
    cd ~/yourCodeDir
    find . -type d -depth 1 -exec git --git-dir={}/.git --work-tree=$PWD/{} pull \;
  }
This one is simple, and was useful in my LAMP dev days, to tail apache logs with line breaks formatted:

  function tailerr() {
    tail -f /path/to/errorlog | sed 's/\\n/\n/g'
  }
This one is pretty useful!

Open current OSX Finder directory in Terminal:

  cdf () {
        target=`osascript -e 'tell application "Finder" to if (count of Finder windows) > 0 then get POSIX path of (target of front Finder window as text)'`
        if [ "$target" != "" ]
        then
                cd "$target"
                pwd
        else
                echo 'No Finder window found' >&2
        fi
}
And `open .` does the opposite: opens Finder to your current directory.
can't you just wite "cd" and drag the folder into the terminal?
Yes, but that's a royal pain. 'cdf' are 3 adjacent keys.
I have a whole repository also including several dotfiles that I maintain for already several years [1]. Following are some snippets from the dotfiles/.alias and dotfiles/.functions files from that repository that probably are the most interesting and which I am using on a regular basis:

    # easier navigation
    alias ..="cd .."
    alias ...="cd ../.."
    alias ....="cd ../../.."
    alias .....="cd ../../../.."
    alias ~="cd ~"

    # Get current public ip address
    alias ip="dig +short myip.opendns.com @resolver1.opendns.com"

    # Alias for summing a column
    alias mksum="paste -sd+ - | bc"

    # Export pbcopy / pbpaste to linux 
    if [[ "${OSTYPE}" == "linux"* ]]; then
        alias pbcopy="xclip -selection clipboard"
        alias pbpaste="xclip -selection clipboard -o"
    fi

    # create a new directory and enter it
    function mkd() {
        mkdir -p "$@" && cd "$_";
    }

    # plot stuff directly from the command line.
    # Example: seq 100 | sed 's/.*/s(&)/' | bc -l | plot linecolor 2
    # -> Generate 100 numbers, wrap it in s(<num>) and calc sin(<num>)
    function plot() {
        { echo 'plot "-"' "$@"; cat; } | gnuplot -persist;
    }

    # wrapper for easy extraction of compressed files
    function extract () {
      if [ -f $1 ] ; then
          case $1 in
              *.tar.xz)    tar xvJf $1    ;;
              *.tar.bz2)   tar xvjf $1    ;;
              *.tar.gz)    tar xvzf $1    ;;
              *.bz2)       bunzip2 $1     ;;
              *.rar)       unrar e $1     ;;
              *.gz)        gunzip $1      ;;
              *.tar)       tar xvf $1     ;;
              *.tbz2)      tar xvjf $1    ;;
              *.tgz)       tar xvzf $1    ;;
              *.apk)       unzip $1       ;;
              *.epub)      unzip $1       ;;
              *.xpi)       unzip $1       ;;
              *.zip)       unzip $1       ;;
              *.war)       unzip $1       ;;
              *.jar)       unzip $1       ;;
              *.Z)         uncompress $1  ;;
              *.7z)        7z x $1        ;;
              *)           echo "don't know how to extract '$1'..." ;;
          esac
      else
          echo "'$1' is not a valid file!"
      fi
    }
[1] https://github.com/NewProggie/Dev-Utils
You might enjoy replacing your extract function with atool (https://www.nongnu.org/atool/) which provides aunpack. It works similarly, but has niceties like creating and unpacking into a subdirectory to avoid accidentally spraying files all over the current working directory if the archive has multiple files at its root.
That extract function seems redundant. GNU tar already auto-detects the compression format of the archive, so `tar xvf archive.tar.xz` will work as expected (the J flag is not needed.) bsdtar does the same, and it supports formats other than tar, so `bsdtar xvf archive.zip` works too. These tools use the content of the file rather than the file extension, so they should be more reliable.
> alias .....="cd ../../../.."

How often do you use that and how often do you get it right? :-)

My cd related aliases are the following. Especially the .<number> is surprisingly useful, although most of the time I only use .2 and .3

  alias ..='cd ../'
  alias .1='cd ../'
  alias .2='cd ../../'
  alias .3='cd ../../../'
  alias .4='cd ../../../../'
  alias .5='cd ../../../../../'
  # sometimes, the space gets swallowed
  alias cd.='cd .'
  alias cd..='cd ..'
But that gave me an idea for another useful directory changer: "go to project root".

Project root definition could vary, although nowadays it is probably "go to the first directory upstream with a .git directory"

Edit: found this elsewhere in this discussion posted by jgresty https://news.ycombinator.com/item?id=18901834

  # go to root git directory
  alias cdgit='cd $(git rev-parse --show-toplevel)'
Used to work primary using docker on my laptop, but been working on some bigger applications that need to be run on beafier machines, so just set environment variable and have these:

    $ cat ~/bin/docker
    #!/bin/bash
    
    if [ "$DOCKER_HOST" != "" ]
    then
      HOST_ARG="-H $DOCKER_HOST"
    fi
    
    /usr/bin/docker $HOST_ARG $@

    $ cat ~/bin/docker-compose
    #!/bin/bash
    
    if [ "$DOCKER_HOST" != "" ]
    then
      HOST_ARG="-H $DOCKER_HOST"
    fi
    
    /usr/local/bin/docker-compose $HOST_ARG $@
This lets you easily search your command history by typing the beginning of a command and pressing the up arrow.

    bind '"\e[A": history-search-backward'
    bind '"\e[B": history-search-forward'
This one automatically lists the files in the current directory, but limits the number of lines.

    function cd {
        builtin cd "${@}"
        if [[ "$(stat -c "%b" .)" -lt "100" ]]
        then
        if [ "$( ls -C -w $COLUMNS | wc -l )" -gt 30 ] ; then
            ls -C -w $COLUMNS --color=always | awk 'NR < 16 { print }; NR == 16 { print " (... snip ...)" }; { buffer[NR % 14] = $0 } END { for( i = NR + 1; i <= NR+14; i++ ) print buffer[i % 14] }'
        else
            ls
        fi
        fi
    }
The first one is pretty much CTRL+R (reverse incremental search)
My most used alias would be:

  alias fuck='sudo $(history -p !!)'
It runs the previous command as root.
(comment deleted)
Didn't see this before I posted, but I have basically the same, but I say please instead of fuck. :)
I have some git ones that save me a few thousand key strokes a day.

  alias gs="git status"
  alias gall="git add ."
  alias gm="git commit"
Out of everything I like this little alias the most:

   cls="clear; ls"
It harkens back to DOS. Clearing the screen followed by ls happens surprisingly often, if not just to start things in "clean slate". I'm quite fond of it.
My muscle memory is to set on CTRL+L, ls. But I like it, and the call back to DOS days :)
ah, I've been using `nyancat -f1`

    function convert-decimaltobinary(){
        n="$1"
        bit=""
        while [ "$n" -gt 0 ]; do
            bit="$(( n&1 ))$bit";
            : $(( n >>= 1 ))
        done
        printf "%s\n" "$bit"
    }
    function convert-binarytodecimal(){
        echo "$((2#$1))"
    }
    function convert-hextodecimal(){
        echo $(( 16#$1 ))
    }
I think I'll use them more if they're cdb, cbd, chd. Thanks!
I am using zsh/iterm on my mac so here are mine:

    # branches that were touched lately 
    nb() {
      git for-each-ref --sort=-committerdate refs/heads/ -- format='%(committerdate:short) %(authorname) %(refname:short)' | head -n 10
    }

    # squash!
    squash() {
      if [ -n "$1" ]; then
        git reset --soft HEAD~$1 && git commit --edit -m"$(git log --format=%B --reverse HEAD..HEAD@{1})"
      fi
    }

    # open pr page on github for current branch
    pr () {
      local repo=`git remote -v | grep -m 1 "(push)" | sed -e "s/.*github.com[:/]\(.*\)\.git.*/\1/"`
      local branch=`git name-rev --name-only HEAD`
      echo "... creating pull request for branch \"$branch\" in \"$repo\""
      open "https://github.com/$repo/pull/new/$branch?expand=1"
    }

    # check who uses port 
    port() {
      lsof -i tcp:"$@"
    }

    # rebases local branch from origin /master and force pushes it
    repush(){
      git fetch origin master
      git stash
      git rebase origin/master
      git push --force
      git stash pop
    }