Bash scripting if statements. Also, running flow in a docker container for Visual Studio Code's flowtype.flow-for-vscode extension.

One of the more annoying things I encountered while learning bash scripting was the examples people used for if statements. Most of the tutorials and examples I followed used if statements with square brackets. Later, I learned that square brackets are only sometimes (or almost never) necessary.

Square brackets are just a shorthand form of the test command, and if just evaluates whether the expression/command that follows it returns a 0.

But commands' return values aren't displayed anywhere in plain sight, so then I learned about the special variable $?, which stores the return value of the previous command.

Basically, if you're using grep to determine whether or not something exists, wrapping it with the test command is unnecessary. grep already returns a 0 when it finds something, and it returns something else when it doesn't. Negating the return value of a grep command only requires that you prepend an exclamation point to the whole thing.

See example below for what I'm talking about. No square brackets anywhere in the if statements, because all I'm testing for is presence or absence of certain strings. Oh, and I wrote the script so Visual Studio Code could use flow from a docker container.

It was balking at my usual script containing just
#!/bin/bash
docker run --rm -v "$PWD":/src -w /src [image]:[version] [command] $*
which I used for lying that I had Golang installed on the system (where image and version were the name and version of a docker image derived from golang:1.9) and [command] was one of the commands Visual Studio Code was complaining about not having (and were contained in the docker image):
    go-outline: go get -u -v github.com/ramya-rao-a/go-outline
    go-symbols: go get -u -v github.com/acroca/go-symbols
    gocode: go get -u -v github.com/nsf/gocode
    godef: go get -u -v github.com/rogpeppe/godef
    godoc: go get -u -v golang.org/x/tools/cmd/godoc
    gogetdoc: go get -u -v github.com/zmb3/gogetdoc
    golint: go get -u -v github.com/golang/lint/golint
    gomodifytags: go get -u -v github.com/fatih/gomodifytags
    gopkgs: go get -u -v github.com/uudashr/gopkgs/cmd/gopkgs
    gorename: go get -u -v golang.org/x/tools/cmd/gorename
    goreturns: go get -u -v sourcegraph.com/sqs/goreturns
    gotests: go get -u -v github.com/cweill/gotests/...
    guru: go get -u -v golang.org/x/tools/cmd/guru
    impl: go get -u -v github.com/josharian/impl
    go play: go get -u -v github.com/haya14busa/goplay/cmd/goplay
There was one more that was not in that list that I also had to install, but I don't remember what it was. I can post the Dockerfile for the image later. Anyway, knowing about $? is also helpful for figuring out how you want to structure commands within if statements.
#!/bin/bash
LOG_FILE=/home/dogi/flow_log

function run_flow {
    echo "Creating flow container" >> $LOG_FILE
    docker run -dti --name "flow_server" -v "$PWD":"$PWD" -w "$PWD" vscode-node:8 bash
}

function start_flow {
    # Don't let crazy process spawners try to spawn flow repeatedly
    if docker ps -a | grep flow_server | grep seconds; then
        echo "Wait a minute" >> $LOG_FILE
        echo "`docker ps -a`" >> $LOG_FILE
        return
    fi

    # Run the flow container if it doesn't exist already.
    if ! docker ps -a | grep flow_server; then
        echo "Flow container doesn't exist; creating" >> $LOG_FILE
        echo "`docker ps -a`" >> $LOG_FILE
        run_flow
    # If the flow container exists, but was started in a different directory
    # (i.e., not found in mounts), destroy it and create it in the current one.
    elif ! echo $(docker inspect -f '{{ .Mounts }}' flow_server) | grep -w "$PWD "; then
        echo "Flow container has wrong mounts" >> $LOG_FILE
        echo "`docker inspect -f '{{ .Mounts }}' flow_server`" >> $LOG_FILE
        if docker ps -a | grep Up | grep flow_server; then
            echo "Flow container is running; killing" >> $LOG_FILE
            echo "`docker ps -a`" >> $LOG_FILE
            docker kill flow_server
        fi
        if docker ps -a | grep flow_server; then
            echo "Flow container exists; deleting" >> $LOG_FILE
            echo "`docker ps -a`" >> $LOG_FILE
            docker rm flow_server
        fi

        run_flow
    # Run the flow container if it already exists and was started in the current
    # directory
    elif ! docker ps -a | grep Up | grep flow_server; then
        echo "Flow container exists in the proper directory but isn't running; restarting container" >> $LOG_FILE
        echo "`docker ps -a`" >> $LOG_FILE
        docker start flow_server
    fi
}

# Suppress docker output. VS Code just wants flow's output.
start_flow > /dev/null

docker exec -i flow_server flow $*

No comments:

Post a Comment