5
\$\begingroup\$

This is a Bash script that runs commands which builds code using the parameters given.

Code

The task:

I have multiple instances of websites which need to be build individually using individual yarn commands and then copied out of a build folder and placed into their own individual folder. I found this very repetitive and so I wanted to solve the problem using a script. I thought about using Python but I ended up using Bash.

Pseudocode:

Inputs: folder where the code is built, folder where the output code is stored, list of languages to run yarn build on.
For each language in input:
   Go to project/build
   Run "yarn build_language"
   Delete output/language folder
   Copy project/build to output/language folder

Here is my Bash implementation:

#!/bin/bash

# PROC Build and Copy:
#     Args:
#         1) Build directory
#         2) Output directory path (general)
#         3) List of language names

#     For each lang in list of language names:
#         Run appropriate build command
#         Delete output directory
#         Copy build directory to output directory/lang

if (( $# < 3 )); then
    echo "Please use at least 3 parameters"
    exit 2
fi

array=("$@")

readonly build_path=$1
readonly output_path=$2
languages=("${array[@]:2}")

build_command="yarn build_"

for lang in "${languages[@]}"
do
    cd "$build_path" || exit
    eval "${build_command}${lang}"
    cd ~- || exit
    output=${output_path}${lang}
    rm -rf "$output"
    mkdir -p "$output"
    cp -r "$build_path" "$output"
done

Sample usage: ./build_all.sh my_project/build/. environments/ python lua

Discussion

I found a style guide on Github and tried to follow it but this is my first Bash script so I am not sure if I followed a good style. I am a Python developer and I fear I may have written a Python-script in Bash. I am also not sure if Bash is the best way to implement this type of script. I wanted to make this re-usable by pulling the directories out into parameters, but I decided not to have the yarn command as a parameter as I thought it could be confusing having lots of parameters. Are my assumptions justified, or do they make the code confusing?

\$\endgroup\$
5
\$\begingroup\$

Language choice

A lot of your commentary on this code revolves around whether bash is a good choice for this project. I would say, yes, it is a good choice. You have shell commands, and you want to make a bit of a wrapper around them. That's a perfect use case for shell, even with so many more recent choices available.

A related concern that you raised is

I am a Python developer and I fear I may have written a Python-script in Bash.

Yeah, maybe, a bit. But that's life in coding. Everything you do is going to be influenced by previous languages you've been exposed to. Most shell programs don't involve arrays, but there's nothing wrong with you taking advantage of unpopular features. I will talk about making this like native shell in the review below.

Code review

  • Your use of indentation and quoting is excellent.
  • Nice variables names. Yay for copying the command line arguments into named variables.
  • Your style guide seems like a reasonable choice although I just skimmed it. I've liked Google's style guide and shellcheck. Shellcheck is a linter so it could be used in your CI pipeline.
  • I like that you exit after some commands that could fail. It would be nice from the calling perspective if you used distinct exit codes. It would also be a good idea to check that rm, mkdir and cp succeeded.
  • Did you try it without evaling the command? I didn't try it, but I'm guessing you can skip the eval.
  • So, back to the arrays being unusual in a shell script... The shell will split a string into arguments if they are separated by spaces. You could make languages be a long space-delimited string and not worry about turning it into an array. The for loop would become for lang in $languages; do and the shell would give you one at a time. It also works your way and it should be decipherable to folks who aren't used to arrays in the shell, so I wouldn't worry about it.
  • The if at the top would usually be expressed like if [[ "$*" < 3 ]]. Your way is fine. The (( )) tells the shell to "do math in here" and that works here, but you might want to do string comparisons or check file existence. Then you'll need the [[ ]].
| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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