Pretty BASH Configuration with IBM i Helpers

    Making BASH Your Default Shell

    There are (at least) a couple of different ways to achieve having BASH on login.

    sshd_config – Globally (not recommended)

    This will make it so anyone that SSHs in will have BASH at login. More information can be found here

    Editsshd_config (usually found at/QOpenSys/QIBM/UserData/SC1/OpenSSH/etc/sshd_config) to have the following:

    # $OpenBSD: sshd_config,v 1.75 2007/03/19 01:01:29 djm Exp $
    
    # This is the sshd server system-wide configuration file.  See
    # sshd_config(5) for more information.
    
    # This sshd was compiled with PATH=/QOpenSys/usr/bin:/usr/ccs/bin:/QOpenSys/usr/bin/X11:/usr/sbin:.:/usr/bin
    
    # The strategy used for options in the default sshd_config shipped with
    # OpenSSH is to specify options with their default value where
    # possible, but leave them commented.  Uncommented options change a
    # default value.
    
    ibmpaseforishell=/path/to/bash # Defines shell for pase. Can be any path to bash you like. For example: /bin/bash

    .profile – Locally (recommended)

    This is recommended as it will only change BASH settings on a per-user basis.

    Put a.profile file in your home directory/home/USERNAME with the following contents:

    #!/bin/bash
    # .profile
    # vim:syntax=sh
    
    # detect if we're in a PASE shell
    /QSYS.LIB/QSHELL.LIB/UNAME.PGM > /dev/null 2>&1
    
    if [ $? != 0 -a "$SHELL" != "/QOpenSys/usr/bin/bash" ]
    then
      exec /QOpenSys/usr/bin/bash
    fi

    Handy Variables, Aliases, and Functions

    I suggest starting everything with some color variables, since they usually end up repeated a lot when creating a decent looking bash environment.

    RESET="\033[0m"
    BOLD="\033[1m"
    BLACK="\033[30m"
    RED="\033[31m"
    GREEN="\033[32m"
    YELLOW="\033[33m"
    BLUE="\033[34m"
    MAGENTA="\033[35m"
    CYAN="\033[36m"
    WHITE="\033[37m"
    BG_BLACK="\033[40m"
    BG_RED="\033[41m"
    BG_GREEN="\033[42m"
    BG_YELLOW="\033[43m"
    BG_BLUE="\033[44m"
    BG_MAGENTA="\033[45m"
    BG_CYAN="\033[46m"
    BG_WHITE="\033[47m"

    That’s usually followed with prettification of PS1 and the most basic aliases.

    In order for this particular PS1 configuration to work
    git-prompt.sh must be used.

    # Prettify the bash prompt. This will give us something like
    # <folder> (<git-branch>) →
    source "${HOME}/git-prompt.sh"
    export PS1="${CYAN}\W${RESET} $(__git_ps1 "(${GREEN}%s${RESET}) ")${RED}→${RESET} "
    
    # Reload bash configuration
    alias reload="source ${HOME}/.bashrc"
    
    # helpful ls aliases
    alias ll='ls -alF'
    alias la='ls -A'
    alias l='ls -CF'

    Then comes all the fun stuff.

    # This is helpful for writing bash scripts.
    # Use: if ask "Continue?" Y;
    function ask() {
        # http://djm.me/ask
        while true; do
    
            if [ "${2:-}" = "Y" ]; then
                prompt="Y/n"
                default=Y
            elif [ "${2:-}" = "N" ]; then
                prompt="y/N"
                default=N
            else
                prompt="y/n"
                default=
            fi
    
            # Ask the question - use /dev/tty in case stdin is redirected from somewhere else
            echo -en "$1 $GREEN($prompt)$RESET "
            read REPLY </dev/tty
    
            # Default?
            if [ -z "$REPLY" ]; then
                REPLY=$default
            fi
    
            # Check if the reply is valid
            case "$REPLY" in
                Y*|y*) return 0 ;;
                N*|n*) return 1 ;;
            esac
    
        done
    }
    
    # Directory Navigation
    alias ..='cd ..'
    alias ...='cd ../..'
    alias ....='cd ../../..'
    alias .....='cd ../../../..'
    alias ......='cd ../../../../..'
    function h { cd ${HOME}/$1; }                         # Use: h Projects/dotfiles
    function d { cd /www/development/developer-name/$1; } # Use: d project-name
    function s { cd /www/stage/$1; }                      # Use: s project-name
    function p { cd /www/production/$1; }                 # Use: p project-name
    
    # Make directory and move to it
    function mkcdr() { mkdir -p $1 && cd $1; }
    
    # Extract
    function extract() {
        if [ -f $1 ] ; then
            case $1 in
                *.tar.bz2)   tar xjf $1 ;;
                *.tar.gz)    tar xzf $1 ;;
                *.bz2)       bunzip2 $1 ;;
                *.rar)       rar x $1 ;;
                *.gz)        gunzip $1 ;;
                *.tar)       tar xf $1 ;;
                *.tbz2)      tar xjf $1 ;;
                *.tgz)       tar xzf $1 ;;
                *.zip)       unzip $1 ;;
                *.Z)         uncompress $1 ;;
                *.7z)        7z x $1 ;;
                *)           echo "'$1' cannot be extracted via extract()" ;;
            esac
        else
            echo "'$1' is not a valid file"
        fi
    }

    Those are just a few examples of handy tools a developer can create when tailoring their bash environment.

    IBM i Specific Highlights

    Thanks @PHPDave for the inspiration.

    # IBM i DB2 alias.
    # Use: db2 "select * from library.file"
    function db2() {
        echo "Running $1";
        system -i "call QSYS/QZDFMDB2 parm('$1')";
    }
    
    # Recursively gives permission for QTMHHTTP to directory passed
    # Use: http-permissions /www/production/project-name/htdocs
    function http-permissions() {
        system -i "CHGAUT OBJ('${1}') USER(QTMHHTTP) DTAAUT(*RWX) OBJAUT(*ALL) SUBTREE(*ALL)";
    }
    
    # Call WRKACTJOB on IBM i
    function wrkactjob() {
        system WRKACTJOB;
    }
    
    # Display the system CCSID value
    function qccsid() {
        system "DSPSYSVAL SYSVAL(QCCSID)";
    }
    
    # Zend Server tool
    function zend-server() {
        case $1 in
            start)
                system "STRTCPSVR SERVER(*HTTP) HTTPSVR(ZENDSVR)"
                ;;
            begin)
                system "STRTCPSVR SERVER(*HTTP) HTTPSVR(ZENDSVR)"
                ;;
            stop)
                system "ENDTCPSVR  SERVER(*HTTP) HTTPSVR(ZENDSVR)"
                ;;
            end)
                system "ENDTCPSVR  SERVER(*HTTP) HTTPSVR(ZENDSVR)"
                ;;
            jobs)
                ps -ef | grep -i zend
                ;;
            *)
                echo $"Usage: zend-server {start|begin|stop|end|jobs}"
        esac
    }

    Those are a few of my favorite ones. Explore other ways to make your own environment more efficient. It’s what makes BASH fun and worthwhile.

    Putting It All Together

    There are a few placeholders in this example configuration. It should be combed through and tailored before use.

    # Example ~/.bashrc
    
    # colors
    RESET="\033[0m"
    BOLD="\033[1m"
    BLACK="\033[30m"
    RED="\033[31m"
    GREEN="\033[32m"
    YELLOW="\033[33m"
    BLUE="\033[34m"
    MAGENTA="\033[35m"
    CYAN="\033[36m"
    WHITE="\033[37m"
    BG_BLACK="\033[40m"
    BG_RED="\033[41m"
    BG_GREEN="\033[42m"
    BG_YELLOW="\033[43m"
    BG_BLUE="\033[44m"
    BG_MAGENTA="\033[45m"
    BG_CYAN="\033[46m"
    BG_WHITE="\033[47m"
    
    # Prettify the bash prompt. This will give us something like
    # <folder> (<git-branch>) →
    source "${HOME}/git-prompt.sh"
    export PS1="${CYAN}\W${RESET} $(__git_ps1 "(${GREEN}%s${RESET}) ")${RED}→${RESET} "
    
    # Reload bash configuration
    alias reload="source ${HOME}/.bashrc"
    
    # helpful ls aliases
    alias ll='ls -alF'
    alias la='ls -A'
    alias l='ls -CF'
    
    # This is helpful for writing bash scripts.
    # Use: if ask "Continue?" Y;
    function ask() {
        # http://djm.me/ask
        while true; do
    
            if [ "${2:-}" = "Y" ]; then
                prompt="Y/n"
                default=Y
            elif [ "${2:-}" = "N" ]; then
                prompt="y/N"
                default=N
            else
                prompt="y/n"
                default=
            fi
    
            # Ask the question - use /dev/tty in case stdin is redirected from somewhere else
            echo -en "$1 $GREEN($prompt)$RESET "
            read REPLY </dev/tty
    
            # Default?
            if [ -z "$REPLY" ]; then
                REPLY=$default
            fi
    
            # Check if the reply is valid
            case "$REPLY" in
                Y*|y*) return 0 ;;
                N*|n*) return 1 ;;
            esac
    
        done
    }
    
    # Directory Navigation
    alias ..='cd ..'
    alias ...='cd ../..'
    alias ....='cd ../../..'
    alias .....='cd ../../../..'
    alias ......='cd ../../../../..'
    function h { cd ${HOME}/$1; }                               # Use: h Projects/dotfiles
    function d { cd /www/development/developer-name/$1; } # Use: d project-name
    function s { cd /www/stage/$1; }                      # Use: s project-name
    function p { cd /www/production/$1; }                 # Use: p project-name
    
    # Make directory and move to it
    function mkcdr() { mkdir -p $1 && cd $1; }
    
    # Extract
    function extract() {
        if [ -f $1 ] ; then
            case $1 in
                *.tar.bz2)   tar xjf $1 ;;
                *.tar.gz)    tar xzf $1 ;;
                *.bz2)       bunzip2 $1 ;;
                *.rar)       rar x $1 ;;
                *.gz)        gunzip $1 ;;
                *.tar)       tar xf $1 ;;
                *.tbz2)      tar xjf $1 ;;
                *.tgz)       tar xzf $1 ;;
                *.zip)       unzip $1 ;;
                *.Z)         uncompress $1 ;;
                *.7z)        7z x $1 ;;
                *)           echo "'$1' cannot be extracted via extract()" ;;
            esac
        else
            echo "'$1' is not a valid file"
        fi
    }
    
    # IBM i DB2 alias.
    # Use: db2 "select * from library.file"
    function db2() {
        echo "Running $1";
        system -i "call QSYS/QZDFMDB2 parm('$1')";
    }
    
    # Recursively gives permission for QTMHHTTP to directory passed
    # Use: http-permissions /www/production/project-name/htdocs
    function http-permissions() {
        system -i "CHGAUT OBJ('${1}') USER(QTMHHTTP) DTAAUT(*RWX) OBJAUT(*ALL) SUBTREE(*ALL)";
    }
    
    # Call WRKACTJOB on IBM i
    function wrkactjob() {
        system WRKACTJOB;
    }
    
    # Display the system CCSID value
    function qccsid() {
        system "DSPSYSVAL SYSVAL(QCCSID)";
    }
    
    # Zend Server tool
    function zend-server() {
        case $1 in
            start)
                system "STRTCPSVR SERVER(*HTTP) HTTPSVR(ZENDSVR)"
                ;;
            begin)
                system "STRTCPSVR SERVER(*HTTP) HTTPSVR(ZENDSVR)"
                ;;
            stop)
                system "ENDTCPSVR  SERVER(*HTTP) HTTPSVR(ZENDSVR)"
                ;;
            end)
                system "ENDTCPSVR  SERVER(*HTTP) HTTPSVR(ZENDSVR)"
                ;;
            jobs)
                ps -ef | grep -i zend
                ;;
            *)
                echo $"Usage: zend-server {start|begin|stop|end|jobs}"
        esac
    }
    
    # Recursively gives permission for QTMHHTTP to directory passed
    # Use: http-permissions /www/production/project-name/htdocs
    function http-permissions() {
        system -i "CHGAUT OBJ('${1}') USER(QTMHHTTP) DTAAUT(*RWX) OBJAUT(*ALL) SUBTREE(*ALL)"
    }

    Taking it all apart

    While it’s acceptable to have everything in one large.bashrc located at the root of one’s home directory, it’s a good idea to split up the configuration into smaller, more manageable files. Configurations can get large, especially when complex functions get thrown into the mix.

    My file structure ends up looking something like:

    ~/
        .bash_start
        .bash_aliases
        .bash_functions
        .bash_end
        .bashrc
        .bash-functions/   <== For more complex bash functions
            zend_server.sh
            ask.sh

    To see how they connect, here are.bashrc,.bash_start, and.bash_functions:

    # .bashrc
    
    source "${HOME}/.bash_start"
    source "${HOME}/.bash_aliases"
    source "${HOME}/.bash_functions"
    source "${HOME}/.bash_end"
    
    # .bash_start
    
    # colors
    RESET="\033[0m"
    BOLD="\033[1m"
    BLACK="\033[30m"
    RED="\033[31m"
    GREEN="\033[32m"
    YELLOW="\033[33m"
    BLUE="\033[34m"
    MAGENTA="\033[35m"
    CYAN="\033[36m"
    WHITE="\033[37m"
    BG_BLACK="\033[40m"
    BG_RED="\033[41m"
    BG_GREEN="\033[42m"
    BG_YELLOW="\033[43m"
    BG_BLUE="\033[44m"
    BG_MAGENTA="\033[45m"
    BG_CYAN="\033[46m"
    BG_WHITE="\033[47m"
    
    # Prettify the bash prompt. This will give us something like
    # <folder> (<git-branch>) →
    source "${HOME}/git-prompt.sh"
    export PS1="${CYAN}\W${RESET} $(__git_ps1 "(${GREEN}%s${RESET}) ")${RED}→${RESET} "
    
    # .bash_functions
    
    # Order matters. It is assumed that `ask` might be used by other functions.
    source "${HOME}/.bash-functions/ask.sh"
    
    # Display the system CCSID value
    function qccsid() {
        system "DSPSYSVAL SYSVAL(QCCSID)";
    }
    
    source "${HOME}/.bash-functions/zend_server.sh"

    💍🦗🙏💻🎱🚲 | They/Them | Coding since age 9 👶➡️👨‍💻 | #Autistic w/ #CharcotMarieTooth | #IBMi + #Linux; #OpenSource #Monk; #BridgingGaps; #IBMChampion | Passionate advocate of open source and its mindset. Business owner and public speaker. Lover of animals, cooking, horror-films, hip-hop, pool, and the Oxford comma; for lists.

    3 Comments

    1. Antonio Ramos

      Nice tutorial.
      I came here because i´m stuck with my bash in iseries
      i installed vue cli and feathersjs
      these 2 when run , should ask users some questions. However the shell closes immediatly and my putty session closes the putty app itself.
      Can you help me understand why?
      if i just use pase, the questions appears but with strange chars that i cannot understand or move up or down.. the arrow.
      Also i can only install node modules inside pase because doing npm install also closes my putty app .
      Any ideas ? where can i get more help ?
      Many thanks
      António

      • Hi Antonio,

        I would need to see the issue first hand and see PuTTY’s behavior to know for sure. Have you tried any other terminal emulator, like cmder?

        I suggest the full version if you want to give cmder a shot, because the full version comes with git. If you try cmder, you could at least narrow it down to if it is PuTTY or SSH that is causing the issue.

        If that doesn’t help, let me know, and we can continue the discussion in emails.

        Regards,
        Josh

    2. Felipe

      Hi Josh.

      Thanks for your tutorial! It was fun a helpful, just one quick note. We’ve installed bash using the new RPM method described in http://ibm.biz/ibmi-rpms and apparently using this method puts a link to bash in the /usr/bin directory so my profile file had to be modified from your version to specify /usr/bin/bash on lines 8 and 10.

      The actual bash file is in “/QOpenSys/pkgs/bin/bash”

      Just in case you want to take that into consideration.

      Thanks again!

    Leave Comment

    Your email address will not be published. Required fields are marked *