Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

I have a script that contains two shell functions:

runhaskells() {
    local DIR=$PWD
    local TARGET="cabal.sandbox.config"
    while [[ ! -e $DIR/$TARGET -a $DIR != "/" ]]; do
        DIR=$(dirname $DIR)
    done
    if [[ $DIR != "/" ]]; then
        local DB=$(sed -ne '/^package-db: */{s///p;q;}' "$DIR/$TARGET")
        runhaskell -no-user-package-db -package-db="$DB" "$@"
    else
        runhaskell "$@"
    fi
} 

ghcs() {
    local DIR=$PWD
    local TARGET="cabal.sandbox.config"
    while [[ ! -e $DIR/$TARGET -a $DIR != "/" ]]; do
        DIR=$(dirname $DIR)
    done
    if [[ $DIR != "/" ]]; then
        local DB=$(sed -ne '/^package-db: */{s///p;q;}' "$DIR/$TARGET")
        ghc -no-user-package-db -package-db="$DB" "$@"
    else
        ghc "$@"
    fi
} 

which obviously is a repeat of each other except for the fact that it calls a different program in each function. The first function calls the runhaskell program while the second function calls the ghc program.

How can I DRY this script (either zsh or bash syntax is fine for me) with the end goal that I should be able to call either function at the command line?

share|improve this question

1 Answer 1

Two simple bash solutions I can think of are:

  1. Use a shell script and $0/$BASH_SOURCE to determine the calling name and a symlink from one name to the other.

  2. Two small wrapper functions.

    h() {
        local DIR=$PWD
        local TARGET="cabal.sandbox.config"
        while [[ ! -e $DIR/$TARGET -a $DIR != "/" ]]; do
            DIR=$(dirname $DIR)
        done
        if [[ $DIR != "/" ]]; then
            local DB=$(sed -ne '/^package-db: */{s///p;q;}' "$DIR/$TARGET")
            set -- "${@:1:1}" -no-user-package-db -package-db="$DB" "${@:2}"
            # or this is the order of original arguments and added arguments don't matter
            set -- "$@" -no-user-package-db -package-db="$DB"
        fi
    
        "$@"
    }
    
    runhaskells() {
        h runhaskell "$@"
    }
    
    ghcs() {
        h ghc "$@"
    }
    

Slightly DRYier version of the above which uses bash's FUNCNAME array.

    h() {
        local DIR=$PWD
        local TARGET="cabal.sandbox.config"
        while [[ ! -e $DIR/$TARGET -a $DIR != "/" ]]; do
            DIR=$(dirname $DIR)
        done
        if [[ $DIR != "/" ]]; then
            local DB=$(sed -ne '/^package-db: */{s///p;q;}' "$DIR/$TARGET")
            set -- "${@:1:1}" -no-user-package-db -package-db="$DB" "${@:2}"
            # or this is the order of original arguments and added arguments don't matter
            set -- "$@" -no-user-package-db -package-db="$DB"
        fi

        "${FUNCNAME[1]%s}" "$@"
    }

    runhaskells() {
        h "$@"
    }

    ghcs() {
        h "$@"
    }
share|improve this answer
1  
Very elegant looking code. Thank you! –  Calvin Cheng Jan 5 at 0:43
    
@CalvinCheng are you sure you don't want to accept this answer (of Etan) instead of the other one? (It's your choice, just checking, in case this was an oversight (it sure looks like one)) –  janos Jan 20 at 13:23
    
@janos The other answer is clearly better in the zsh universe. –  Etan Reisner Jan 20 at 13:40

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.