While building Rust Adventure (a temporary name for a Rust course I'm working on), I found running cargo new
repeatedly, so I decided to automate it with bash.
#!/bin/bashset -euxo pipefailLAST_FILE=`fd ^a- 'adventures/' -t d -d 1 | sort | tail -n 1`regex="^adventures/a-([[:digit:]]+)"if [[ $LAST_FILE =~ $regex ]]; thennumber="${BASH_REMATCH[1]}"number=$((number+1))formatted_number=$(printf "%03d" $number)cargo new adventures/a-$formatted_number-$1 --name $1elseecho "$LAST_FILE doesn't match $regex"fi
Using fd
, we can see a sample layout for the project. In ./adventures
we have a set of cargo crates as a workspace.
➜ fd . adventures/ -d1adventures/a-001-hello-worldadventures/a-002-variablesadventures/a-003-mutable-variablesadventures/a-004-variable-types
First in our script we set the shebang
, aka the #!
, to use the bash interpreter. (yes it's really called that, no I don't know why).
#!/bin/bash
Then we set -euxo pipefail
, which other people have already explained well. TLDR; it makes our bash script a bit safer to run, and prints each command as it executes.
set -euxo pipefail
Now we start getting into our script logic. We set LAST_FILE
equal to the last result in a sorted list created by fd
. This gives us the "highest number" in the filenames, since they're effectively sorted by number.
The fd
is looking for directories (-t d
) one level deep (-d 1
) in adventures
that match ^a-
.
LAST_FILE=`fd ^a- 'adventures/' -t d -d 1 | sort | tail -n 1`
We want to get the highest number from the list of directories and increment it. To do this we'll use regex. To regex in bash, we can set up our regex as a variable with a string. This looks for any numbers after adventures/a-
and puts them in a capture group.
regex="^adventures/a-([[:digit:]]+)"
Then test our filename against the regex to see if it matches.
if [[ $LAST_FILE =~ $regex ]]; then# it matched the regex, so we can use the capture group nowelseecho "$LAST_FILE doesn't match $regex"fi
I put the capture group result into a new variable number
for clarity, then add 1
to it. We can overwrite the variable here because we won't be using the original again.
number="${BASH_REMATCH[1]}"number=$((number+1))
If we leave the number as-is, then any number below 100 won't have three digits. To fix this we can use printf
to pad the number with leading zeroes. This leaves us with numbers like 005
or 040
.
formatted_number=$(printf "%03d" $number)
and finally we can use our variables along with a positional argument to the script to instantiate our cargo new
.
cargo new adventures/a-$formatted_number-$1 --name $1
The output could look like this.
➜ ./scripts/new-adventure.sh something++ fd '^a-' adventures/ -t d -d 1++ sort++ tail -n 1+ LAST_FILE=adventures/a-004-variable-types+ regex='^adventures/a-([[:digit:]]+)'+ [[ adventures/a-004-variable-types =~ ^adventures/a-([[:digit:]]+) ]]+ number=004+ number=5++ printf %03d 5+ formatted_number=005+ cargo new adventures/a-005-something --name somethingCreated binary (application) `something` package