Parameterized comparisons (Bash)
Naar navigatie springen
Naar zoeken springen
How to make a comparison so flexible, that the arguments could be provided as an argument to a function?
Context
- I frequently use code to loop through an associative array with 'rows' (associative arrays in Bash don't really have rows) representing websites on a webserver. In this loop, these rows are filtered and sites that pass, are processed in some way
- This filter is hardcoded. That's messy. I would like to completely abstract away this loop.
Some new insights (autumn 2023):
- It would be nice if the filtered list of sites, would actually be another file, stored on the computer: GNU Parallel cannot handle associative arrays. I have examples where the data concerning the filtered sites, is subsequently copied to a normal array (or even a variable), but I think that using a file is actually easier for debugging
- I don't like it at all, that the main part of these scripts is a loop and that a function is called from within this loop. I can't put my finger on it, but I really don't like loops. Maybe this is after I saw a YouTube video about 'never nesters'. Using GNU Parallel solves two problems at once: (1) Get rid of loops and (2) speed up execution.
Example of the filter withing this 'site loop':
if \ [[ ${site_array[$i,tag]} =~ "_dvb8v2_" ]] && \ [[ ${site_array[$i,tag]} =~ "_wc_" ]] && \ [[ ${site_array[$i,tag]} =~ "_top14_" ]] && \ [[ ${site_array[$i,language]} ="en" # then ... fi
Solutions
Separate comparison function
It might help to think of using a separate function that only contains the comparison and that returns a Boolean Yes or No - That makes stuff less complex. E.g.:
function filter_site_array_row() { local conditions="$1" local tag="$2" if [[ $tag =~ $conditions ]]; then return 0 # Condition matched else return 1 # Condition not matched fi }
eval
Bash has something like eval
, where a variable can be executed as code. Very flexible, but quite impossible to debug + prone to errors. Security isn't an issue here.
Pass conditions & arguments
Simple example:
#!/bin/bash filter_conditions() { local conditions="$1" local tag="$2" if [[ $tag =~ $conditions ]]; then return 0 # Condition matched else return 1 # Condition not matched fi } # Example usage: site_array=( [0,tag]="_dvb8v2_bal_wp_wc_cb_top14_" [1,tag]="_dvb8v2_wp_wc_cb_top14_" [2,tag]="_dvb8v2_bal_wc_cb_top14_" ) # Define conditions based on arguments passed to the script conditions="_dvb8v2_.*_wp_.*_wc_.*_cb_.*_top14_" # Loop through site_array and apply filter_conditions for ((i = 0; i < ${#site_array[@]}; i++)); do if filter_conditions "$conditions" "${site_array[$i,tag]}"; then echo "Match found for index $i: ${site_array[$i,tag]}" # Do something with the matched tag fi done
However, this can only handle comparisons concerning the tag
field of rows. A more flexible approach:
#!/bin/bash filter_conditions() { local conditions=("$@") local tag="${site_array[$i,tag]}" local language="${site_array[$i,language]}" for condition in "${conditions[@]}"; do if [[ $condition == "tag_"* ]]; then local field="${condition:4}" # Extract the field name from the condition if [[ ! $tag =~ "${!field}" ]]; then return 1 # Condition not matched for tag field fi elif [[ $condition == "language_"* ]]; then local field="${condition:9}" # Extract the field name from the condition if [[ "${!field}" != "en" ]]; then return 1 # Condition not matched for language field fi else # Invalid condition format echo "Invalid condition: $condition" return 1 fi done return 0 # All conditions matched } # Example usage: site_array=( [0,tag]="_dvb8v2_wc_top14_" [0,language]="en" [1,tag]="_dvb8v2_wp_wc_top14_" [1,language]="fr" [2,tag]="_dvb8v2_wc_top14_" [2,language]="en" ) # Define conditions based on arguments passed to the script conditions=( "tag_.*_wc_.*_top14_" # Condition for tag field "language_en" # Condition for language field ) # Loop through site_array and apply filter_conditions for ((i = 0; i < ${#site_array[@]}; i += 2)); do if filter_conditions "${conditions[@]}"; then echo "Match found for index $i: Tag - ${site_array[$i,tag]}, Language - ${site_array[$i,language]}" # Do something with the matched fields fi done