Extract PlatformIO Environments To Bash Array With Awk
Introduction
In this article, we will explore how to extract environments defined in a platformio.ini
file and store them into a Bash array. This is particularly useful when you need to loop through different environments and perform specific actions before building your project. PlatformIO is a powerful open-source ecosystem for IoT development, and managing multiple environments is a common requirement. By leveraging command-line tools like awk
, we can efficiently parse the configuration file and automate tasks. This guide will provide a detailed walkthrough, ensuring you understand each step and can apply it to your projects.
Understanding the PlatformIO Configuration File
Before diving into the solution, let's briefly discuss the structure of a platformio.ini
file. This file uses an INI-like format, where sections are defined within square brackets ([]
), and each section represents an environment. Within each environment, various configuration options can be set. For example, you might define environments for different boards or build configurations. Understanding this structure is crucial for accurately extracting the environment names. Each environment name typically follows the pattern [env:environment_name]
. Our goal is to extract environment_name
from each of these sections. This extracted information can then be used in a Bash script to automate builds, tests, or deployments for each environment.
Why Use Awk?
Awk is a powerful text-processing tool that is ideal for parsing structured text files. It operates by scanning each line of a file and applying a set of actions based on specified patterns. In our case, we can use awk
to identify lines that define environments (i.e., lines starting with [env:
) and extract the environment name. Awk's built-in string manipulation functions make it easy to strip away the unwanted characters and isolate the environment name. Moreover, awk
is readily available on most Unix-like systems, making it a portable solution for scripting. Alternatives like sed
or grep
could also be used, but awk
provides a more concise and readable solution for this particular task. Its ability to handle patterns and perform actions on matched lines makes it a perfect fit for parsing configuration files like platformio.ini
.
Step-by-Step Guide to Extracting Environments
Now, let's walk through the steps required to extract the environments from a platformio.ini
file into a Bash array. We'll break down the process into manageable parts, explaining each step in detail.
1. Examining the platformio.ini
File
First, let's assume you have a platformio.ini
file that looks something like this:
; PlatformIO Project Configuration File
; Read more: https://docs.platformio.org/page/projectconf.html
[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
[env:nodemcuv2]
platform = espressif8266
board = nodemcuv2
framework = arduino
[env:my_custom_board]
platform = myplatform
board = myboard
framework = myframework
This is a typical PlatformIO configuration file, where each [env:...]
section defines a different environment. Our objective is to extract the environment names esp32dev
, nodemcuv2
, and my_custom_board
.
2. Using Awk to Extract Environment Names
The core of our solution lies in using awk
to parse the file and extract the relevant information. Here's the awk
command we'll use:
awk -F'[:\]]' '/^[[:space:]]*${env:/{print $2}' platformio.ini
Let's break down this command:
awk
: Invokes theawk
utility.-F'[:}$]'
: Sets the field separator to either:
or]
. This means thatawk
will split each line into fields based on these delimiters./^[[:space:]]*${env:/
: This is the pattern thatawk
will search for. Let's dissect it further:^
: Matches the beginning of the line.[[:space:]]*
: Matches zero or more whitespace characters.\[env:
: Matches the literal string[env:
. The backslash is used to escape the special character[
.
/{print $2}
: This is the action to perform when the pattern is matched.print $2
prints the second field, which, given our field separators, will be the environment name.platformio.ini
: Specifies the input file.
This command will output the list of environment names, each on a new line:
esp32dev
nodemcuv2
my_custom_board
3. Storing the Environments in a Bash Array
Now that we have the list of environment names, we need to store them in a Bash array. We can achieve this using command substitution and the mapfile
command (or its older equivalent, a while
loop with read
).
Using mapfile
The mapfile
command reads lines from standard input and assigns them as elements of an array. Here's how to use it:
mapfile -t environments < <(awk -F'[:}$]' '/^[[:space:]]*${env:/{print $2}' platformio.ini)
mapfile -t environments
: Reads lines from standard input and appends them to the arrayenvironments
. The-t
option removes trailing newlines.< <(...)
: This is process substitution, which takes the output of the command inside the parentheses and makes it available as a file for input.awk -F'[:}$]' '/^[[:space:]]*${env:/{print $2}' platformio.ini
: This is the sameawk
command we used earlier to extract the environment names.
Alternative: Using a while
Loop and read
For compatibility with older Bash versions (before mapfile
was introduced), you can use a while
loop and the read
command:
environments=()
i=0
while IFS= read -r env_name;
do
environments[$i]="$env_name"
i=$((i+1))
done < <(awk -F'[:}$]' '/^[[:space:]]*\[env:/{print $2}' platformio.ini)
environments=()
: Initializes an empty array.i=0
: Initializes a counter variable.while IFS= read -r env_name; do ... done
: This loop reads each line from the output of theawk
command into theenv_name
variable.IFS=
: Preventsread
from stripping leading/trailing whitespace.-r
: Prevents backslash escapes from being interpreted.
environments[$i]="$env_name"
: Assigns the environment name to the array at indexi
.i=$((i+1))
: Increments the counter.< <(...)
: Process substitution, as explained earlier.
4. Verifying the Array Contents
To verify that the environments have been successfully stored in the array, you can print the array contents using the following command:
echo "${environments[@]}"
This will output the environment names separated by spaces:
esp32dev nodemcuv2 my_custom_board
To print each environment name on a new line, you can use:
printf '%s\n' "${environments[@]}"
5. Looping Through the Environments
Now that we have the environments in an array, we can easily loop through them and perform actions. For example, let's print each environment name along with a message:
for env in "${environments[@]}"; do
echo "Processing environment: $env"
# Perform your actions here
done
This loop iterates through each element in the environments
array and prints a message. You can replace the # Perform your actions here
comment with any commands you want to execute for each environment. This could include building the project, running tests, or deploying the firmware.
Practical Examples and Use Cases
Let's explore some practical examples of how you can use this technique in your PlatformIO projects.
1. Building Projects for Multiple Environments
One common use case is building your project for multiple environments. You can use the extracted environment names to pass to the platformio run
command. Here's an example:
for env in "${environments[@]}"; do
echo "Building environment: $env"
pio run -e "$env"
done
This script will loop through each environment and execute the platformio run
command, building the project for each specified environment. This is particularly useful when you are targeting multiple hardware platforms or have different build configurations.
2. Running Tests on Different Environments
Another use case is running tests on different environments. You might have a suite of tests that you want to run on each platform to ensure compatibility and stability. Here's how you can do it:
for env in "${environments[@]}"; do
echo "Testing environment: $env"
pio test -e "$env"
done
This script will loop through each environment and execute the platformio test
command, running the tests for each specified environment. This helps ensure that your code works correctly across different platforms and configurations.
3. Deploying Firmware to Multiple Devices
If you are working on a project that involves deploying firmware to multiple devices, you can use the extracted environments to automate the deployment process. Here's an example:
for env in "${environments[@]}"; do
echo "Deploying to environment: $env"
pio run -t upload -e "$env"
done
This script will loop through each environment and execute the platformio run -t upload
command, deploying the firmware to each specified environment. This can significantly streamline the deployment process, especially when you have a large number of devices to update.
Optimizing the Script for Performance and Readability
While the above examples are functional, there are ways to optimize the script for better performance and readability. Let's discuss some improvements.
1. Error Handling
It's always a good practice to add error handling to your scripts. You can check the exit status of commands and take appropriate actions if an error occurs. Here's an example:
for env in "${environments[@]}"; do
echo "Building environment: $env"
pio run -e "$env" || {
echo "Error building environment: $env"
exit 1
}
done
This script will exit with an error if the pio run
command fails for any environment. The || { ... }
construct executes the commands within the curly braces if the preceding command returns a non-zero exit status (indicating an error).
2. Using Functions for Reusability
If you have a complex script with multiple actions to perform for each environment, it's a good idea to encapsulate those actions in functions. This improves the readability and maintainability of your script. Here's an example:
build_environment() {
local env=$1
echo "Building environment: $env"
pio run -e "$env" || {
echo "Error building environment: $env"
return 1
}
}
for env in "${environments[@]}"; do
build_environment "$env" || exit 1
done
This script defines a function build_environment
that takes an environment name as an argument and builds the project for that environment. The main loop then calls this function for each environment. This makes the script more modular and easier to understand.
3. Adding Verbose Output
To provide more informative output, you can add verbose messages to your script. This can be helpful for debugging and monitoring the progress of your script. Here's an example:
for env in "${environments[@]}"; do
echo "[INFO] Processing environment: $env"
pio run -e "$env" 2>&1 | tee "build_$env.log" || {
echo "[ERROR] Error building environment: $env"
exit 1
}
done
This script adds log messages to indicate the start of processing for each environment. It also redirects the output of the pio run
command to a log file using tee
, which allows you to both see the output in the console and save it to a file. The 2>&1
redirects standard error to standard output, ensuring that all output is captured.
Conclusion
In this article, we've covered how to extract PlatformIO environments to a Bash array using awk
. We've walked through the process step by step, from examining the platformio.ini
file to looping through the environments and performing actions. We've also discussed practical examples and optimizations to make your scripts more robust and efficient. By using these techniques, you can automate your PlatformIO workflows and streamline your development process. Whether you're building, testing, or deploying your projects, scripting with Bash and awk
can save you time and effort. Remember to adapt these techniques to your specific needs and explore the many other possibilities that PlatformIO and Bash scripting offer.