1. 9

What unique compositions of common Unix commands do you use often?

Here’s one I use to see which commands I’m running most often:

alias mostusedcomms="history \
    | awk '{CMD[\$2]++;count++;}END { for (a in CMD)print CMD[a] \" \" CMD[a]/count*100 \"% \" a;}' \
    | grep -v \"./\" | column -c3 -s \" \" -t | sort -nr | nl |  head -n20"

Or one which prints giant text to the screen, to let me know when long running jobs are done:

#!/bin/bash

if [ $# -eq 1 ]; then
    MSG="$1"
else
    MSG="done"
fi
osascript -e "tell application \"Quicksilver\" to show large type \"$MSG\""
  1.  

  2. 3

    When I’m in doubt about shell expansion, I usually run echo-eol which is like this:

    #!/usr/bin/perl -w
    #
    # Just to verify how command line arguments work in weird cases.
    
    use strict;
    use warnings;
    
    my $i = 0;
    foreach my $arg (@ARGV) {
        printf "%03d> '%s'\n", $i++, $arg;
    }
    

    Also, since at work I’ve got a secondary monitor, I use the workscreen commands, which is a shorthand for xrandr:

    #!/bin/sh
    
    export LAPTOP="LVDS-1"
    export SCREEN="DP-1"
    
    case $1 in
    
        "dual")
            xrandr --output $SCREEN --auto
            xrandr --output $LAPTOP --auto
            xrandr --output $SCREEN --above $LAPTOP
        ;;
    
        "laptop")
            xrandr --output $LAPTOP --auto
            xrandr --output $SCREEN --off
        ;;
    
        "screen")
            xrandr --output $SCREEN --auto
            xrandr --output $LAPTOP --off
        ;;
    
        *)
            echo "laptop | screen | dual"
        ;;
    
    esac
    

    As Vim user, I don’t like caps lock and I prefer esc instead…

    #!/bin/sh
    
    case $1 in
      "enable")
        xmodmap -e 'clear lock'
        xmodmap -e 'keycode 0x42 = Escape'
        ;;
      "disable")
        xmodmap -e 'keycode 0x42 = Caps_Lock'
        xmodmap -e 'add lock = Caps_Lock'
        ;;
      *)
        echo "usage: $0 enable|disable"
    esac
    

    …those are the most interesting home-made commands of mine. I’ve got some other commands which are more complex, but they’re very specific for my routine.

    1. 2

      I frequently use the classic overwrite from K&P:

      #!/bin/sh
      # overwrite:  copy standard input to output after EOF
      # (final version)
      
      # set -x
      
      case $# in
      0|1)    echo 'Usage: overwrite file cmd [args]' 1>&2; exit 2
      esac
      
      file=$1; shift
      new=/tmp/$$.new; old=/tmp/$$.old
      trap 'rm -f $new $old; exit 1' 1 2 15   # clean up files
      
      if "$@" >$new       # collect input
      then
          cp $file $old   # save original file
          trap 'trap "" 1 2 15; cp $old $file # ignore signals
                rm -f $new $old; exit 1' 1 2 15   # during restore
          cp $new $file
      else
          echo "overwrite: $1 failed, $file unchanged" 1>&2
          exit 1
      fi
      rm -f $new $old
      

      For example, while json_reformat < test.json > test.json won’t work, overwrite test.json json_reformat < test.json will.

      I also use this handy utility for converting plain text files wrapped at 80 columns to PDF, with a header that includes the file name: (requires enscript and ps2pdf from ghostscript)

      #!/bin/sh
      
      first=0
      for f; do
        if [ $first != 0 ]; then
          echo '--------------------------------------------------------------------------------'
          first=1
        fi
      
        echo "File: " $f
        echo
        cat $f
      done | enscript --font="DejaVuSansMono11" --media Letter \
               --margins=25:25:: \
               --header='$n|Page $% of $=' -o - | ps2pdf - -
      

      Finally, I bind edit_textbuf to Super-Shift-e, which automatically loads text from a textarea in a browser into vim in a terminal. (And when you quit vim, it overwrites the textarea with your modifications.) I believe the script can be configured for other terminals/editors.

      1. 2

        https://github.com/leifwalsh/df/tree/master/bin

        All of that is cruft I no longer use, except for mypass:

        #!/usr/bin/env bash
        
        if [[ $(uname -s) = "Darwin" ]]
        then
            COPY=pbcopy
        else
            COPY="xclip -sel clipboard"
        fi
        SHA256=$(which sha256sum)
        if [ $? != 0 ] ; then
            SHA256=$(which sha256)
        fi
        
        if [[ $# -ne 1 ]]; then echo "Usage: $(basename $0) <site>" && exit 2; fi
        (gpg -q -d ~/.passwd.gpg; echo "$1") | $SHA256 | cut -c-16 | xargs echo -n | $COPY
        

        It’s my own little 1password/lastpass/keepass replacement.

        1. 2

          A long time ago, I started putting all the random C utilities and shell scripts I was writing for myself in a git repo.

          I haven’t really used most of those utilities in a while. This post prompted me to go back and document some of them more properly. Some useful ones are

          webpipe.sh - Pipe standard input into an HTML file and open it in the browser. So you could do something like redcarpet README.md | webpipe.sh to see what your README will look like on Github.

          pdfcat.sh - Basically an alias for a hairy invocation of GhostScript that will combine several PDF documents together.

          lrange - The middle sibling of head and tail; lrange a b file.txt will print the lines a-b inclusive from the file to standard output.

          1. 2

            I’ve dropped Larry's filename fixer into ~/bin for as long as I can remember. It’s from the Perl Cookbook and that Larry.

            #!/usr/bin/perl -w
            # rename - Larry's filename fixer
            $op = shift or die "Usage: rename expr [files]\n";
            chomp(@ARGV = <STDIN>) unless @ARGV;
            for (@ARGV) {
                    $was = $_;
                    eval $op;
                    die $@ if $@;
                    rename($was,$_) unless $was eq $_;
            }
            

            It lets you apply perlre to file renaming operations:

            $ rename 's/foo-(\d+)/$1-bar/' *.baz
            

            More examples and notes here.

            Unfortunately my ~/bin also contains cvs-wtf-arg:

            #!/bin/sh
            cvs up 2>/dev/null | grep "^\?"
            

            because I work on one project that still uses CVS. If you know why I need this then I apologise.

            1. 2

              The projectors at my school are all connected to some hub thing which make my Arch install only detect 800x600 as a possible resolution (sometimes smaller). I got sick of writing the commands to force it to output at 1920x1080 and mirror my laptop’s built in display so I put together a script to do it for me. It’s just three xrandr commands, but it’s so nice.

              I also have a tiny python script to upload a specified file to a “stuff I want to share quickly” site.

              1. 1

                I rarely remember what our latest release was. I keep this around under git-latest-tag so I can quickly check. It relies on GNU sort’s version sort, so for Mac users gsort is available on Homebrew in the coreutils package.

                #!/bin/sh
                PREFIX="$1"
                git tag | grep "^$PREFIX[0-9]" | gsort -V | tail -n1
                

                I also spend a lot of time managing Redis instances, so I keep this in redis-delkeys. It removes all the keys in a database matching a pattern. It takes the same switches as redis-cli for host, port, and db number. I have a couple variants of it for different tasks (moving keys between two different instances, etc.), but they all have this general structure.

                #!/bin/sh
                set -e
                HOST="localhost"
                PORT="6379"
                DB="0"
                while getopts "h:p:n:" opt; do
                    case $opt in
                        h)  HOST=$OPTARG;;
                        p)  PORT=$OPTARG;;
                        n)  DB=$OPTARG;;
                        \?) echo "invalid option: -$OPTARG" >&2; exit 1;;
                    esac
                done
                shift $(( $OPTIND -1 ))
                PATTERN="$@"
                if [ -z "$PATTERN" ]; then
                    echo "pattern required" >&2
                    exit 2
                fi
                redis-cli -h $HOST -p $PORT -n $DB --raw keys $PATTERN |
                    xargs redis-cli -h $HOST -p $PORT -n $DB del
                
                  1. 1

                    I get a lot of mileage from my git-deploy script. It’s pretty simple, just force-pushes the current HEAD onto a given remote.

                    #!/bin/bash
                    git push $1 +HEAD:master
                    git fetch $1
                    git push origin --tags
                    

                    This assumes you use git for deployments. You’ll take some serious heat from your fellow developers if you used this on a remote that isn’t a “functional” repository (I classify most repos as archival, deployments as functional).

                    The latter two lines fetch the history of the remote and push any auto-generated deployment tags with the origin.This useful when git log --decorateing to see what is live compared to what is in dev.

                    1. [Comment removed by author]

                      1. 2

                        I use this (in my ~/.gitconfig) to prevent myself having to write out the remote names:

                        [alias]
                          mpush = !git remote | xargs -J % -L 1 git push % master
                        
                        1. 1

                          If you named it “git-pushall” or something then you could use “git pushall” like any other git command, fwiw.

                          1. 1

                            Would that work? Wouldn’t you need to call git-pushall not git pushall? Because at least for me, I don’t have a git-push or git-pull command, which makes me think they’re just nice ways to separate sections of the manual.

                            You could write an alias, which would let you use git pushall:

                            [alias]
                              pushall = "!git push digitalocean master && git push github master && git push bitbucket master"
                            
                            1. 2

                              It works, give it a try. The main git binary will look for a git-FOO executable for you.

                              Because at least for me, I don’t have a git-push or git-pull command

                              You actually do, it’s just not on your standard path. git knows where to look for it, on my system for example there is /usr/lib/git-core/git-pull and that git-core dir is full of the same.

                        2. 1

                          Browse cvsweb for current directory or named file:

                          #!/bin/sh
                          firefox -new-window http://www.openbsd.org/cgi-bin/cvsweb/`cat CVS/Repository`/$1
                          

                          Simple alternative to ag or ack:

                          #!/bin/sh
                          target=$1
                          shift
                          places=$@
                          if [ -z "$places" ] ; then places=. ; fi
                          find $places  \! \( -path "*/obj/*" -or -path "*/CVS/*" \) \! \( -name "*.o" -or -name "*.orig" -or -name ".#*" \) \! -type l -print0 | xargs -0 grep "$target"