I have written a script, that reformats the output of qstat -f1 of the PBS queueing system. Unfortunately this project is by far too big to post here complete, also I am still working on it.The purpose is to have a quick overview of what is running by whom and what resources are blocked. I am am also using chains, where certain jobs are waiting on the completion of another job. These will be put on hold by the queueing system. However, sometimes it is necessary to manually put a job on hold, because there was a more pressing one. Without looking through the whole output I wanted a way to determine which jobs are depending on each other, so that I don't accidentally release a job that does not have the right prerequisites and crashes on start-up.  I have therefore written the following subroutines to resolve such dependencies and list them. It is working quite well for me, but I am still wondering if I have mistakes in it. I cut the script together to a minimal working part, as well as a dummy routine to simulate the output (which is truncated).#!/bin/bash# Prepping for the output: truncate or expand variable for later formatting#truncExpVar (){    local varcontent=$1    local -i varlength=$2    local varname=$3    local alignment=$4    local -i startdigits    local -i enddigits    if (( $varlength < 3 )); then fatal Fatality. Scipt needs debugging.; fi    if (( ${#varcontent} > $varlength )); then        if (( $varlength < 10 )); then startdigits=1; else startdigits=3; fi        enddigits=$((varlength - startdigits -1))        printf -v $varname %s\~%s ${varcontent:0:$startdigits} ${varcontent:(-$enddigits)}    else        if [ $alignment == left ]; then            printf -v $varname %-*s $varlength $varcontent        else            printf -v $varname %*s $varlength $varcontent        fi    fi}#Parse input line for the Job IDgetJobID (){    if [[ $1 =~ Job[[:space:]]Id:[[:space:]]+([0-9]+)\.([0-9a-z]+).* ]]; then        fulljobid=${BASH_REMATCH[0]}        longjobid=${BASH_REMATCH[1]}.${BASH_REMATCH[2]}        shortjobid=${BASH_REMATCH[1]}    else        return 2    fi    truncExpVar $longjobid 20 longjobid left    truncExpVar $shortjobid 6 shortjobid right}truncDependencyList (){    local truncContent=$1    local varname=$2    local varlist    while [[ $truncContent =~ :([0-9]+)\.(.*) ]]; do        varlist=$varlist ${BASH_REMATCH[1]}        truncContent=${BASH_REMATCH[2]}    done    printf -v $varname %s:%s ${varname:0:1} $varlist}getDependencies (){    local afterok beforeok    local pattern=^[[:space:]]*depend = (beforeok|afterok)(:[^,]+)[,]?(beforeok|afterok)?([:]?.*)$    if [[ $1 =~ $pattern ]]; then        truncDependencyList ${BASH_REMATCH[2]} ${BASH_REMATCH[1]}        [[ ! -z ${BASH_REMATCH[3]} ]] && truncDependencyList ${BASH_REMATCH[4]} ${BASH_REMATCH[3]}        dependencies=$afterok $beforeok    else        return 2    fi}printline (){    echo $fulljobid $longjobid $shortjobid    echo $dependencies    echo ###    unset fulljobid longjobid shortjobid dependencies}analyzeThis (){    while read line; do    getJobID $line; [[ $? == 0 ]] && continue    getDependencies $line; [[ $? == 0 ]] && continue#set +x    if [[ -z $line ]]; then        printline    fi    done}# For demonstration purpose. Output will come from#   qstat -f1 $includebobids# for the PBS queueing systemdummyInput (){#Truncated outputcat << EOFJob Id: 4765.foo123@bar.ac.jp    Job_Name = h2o    Job_Owner = martin@foo123@bar.ac.jp    job_state = Q    [...]    depend = beforeok:4766.foo123@bar.ac.jp@foo123@bar.ac.jp:4768.foo123@bar.ac.jp@foo123@bar.ac.jp    [...]    submit_host = foo123@bar.ac.jp    init_work_dir = /home/martin/tester/test-waterJob Id: 4766.foo123@bar.ac.jp    Job_Name = h2o.freq    Job_Owner = martin@foo123@bar.ac.jp    job_state = H    [...]    depend = afterok:4765.foo123@bar.ac.jp@foo123@bar.ac.jp,beforeok:4767.foo123@bar.ac.jp@foo123@bar.ac.jp:4768.foo123@bar.ac.jp@foo123@bar.ac.jp    [...]    submit_host = foo123@bar.ac.jp    init_work_dir = /home/martin/tester/test-waterJob Id: 4767.foo123@bar.ac.jp    Job_Name = h2o.nbo    Job_Owner = martin@foo123@bar.ac.jp    job_state = H    [...]    depend = afterok:4766.foo123@bar.ac.jp@foo123@bar.ac.jp    [...]    submit_host = foo123@bar.ac.jp    init_work_dir = /home/martin/tester/test-waterJob Id: 4768.foo123@bar.ac.jp    Job_Name = h2o.nbo    Job_Owner = martin@foo123@bar.ac.jp    job_state = H    [...]    depend = afterok:4766.foo123@bar.ac.jp@foo123@bar.ac.jp:4765.foo123@bar.ac.jp@foo123@bar.ac.jp    [...]    submit_host = foo123@bar.ac.jp    init_work_dir = /home/martin/tester/test-waterJob Id: 4769.foo123@bar.ac.jp    Job_Name = h2o    Job_Owner = martin@foo123@bar.ac.jp    job_state = Q    [...]    submit_host = foo123@bar.ac.jp    init_work_dir = /home/martin/tester/test-waterEOF}analyzeThis < <(dummyInput)
Resolve dependencies from output of PBS queueing system
parsing;bash;subroutine
Use exit code directlyThis is a bit awkwardly written:getJobID $line; [[ $? == 0 ]] && continueThe natural (and simpler) way to write this would be:getJobID $line && continueUser-friendlinessThis error message doesn't look very useful:if (( $varlength < 3 )); then fatal Fatality. Scipt needs debugging.; fiIt would be better to include in the error message a hint about the problem,in this case that $varlength should be >= 3.(There's also a typo in Scipt.)ReadabilityThe truncExpVar function could use some blank lines to make it slightly easier to read.For example you could add a blank line after the local variable declarations,and one more after the $varlength validation.In analyzeThis it would be better to indent the body of the while loop.Actually I would rewrite that function like this:analyzeThis (){    while read -r line; do        getJobID $line && continue        getDependencies $line && continue        [[ $line ]] || printline    done}One-liner ifSome one-liner if statements could be written slightly simpler, for example this one:if (( $varlength < 3 )); then fatal Fatality. Scipt needs debugging.; fiCould be written more compactly like this:(( varlength < 3 )) && fatal Fatality. Scipt needs debugging.Math in BashYou can simplify some of the math, by dropping $ inside ((...)),for example instead of:if (( $varlength < 3 )); then fatal Fatality. Scipt needs debugging.; fiif (( ${#varcontent} > $varlength )); then    if (( $varlength < 10 )); then startdigits=1; else startdigits=3; fi    enddigits=$((varlength - startdigits -1))You can write:if (( varlength < 3 )); then fatal Fatality. Scipt needs debugging.; fiif (( ${#varcontent} > varlength )); then    if (( varlength < 10 )); then startdigits=1; else startdigits=3; fi    ((enddigits = varlength - startdigits - 1))== operator in [ ... ] ?help [ tells that [ ... ] is an alias to test, and help test only explains the = operator and says nothing about ==.So to be safe, I would replace == with = in this code:    if [ $alignment == left ]; thenshellcheckhttp://www.shellcheck.net/# is a great site where you can copy-paste your Bash scripts and it will point out code smells.