Home > Net >  How can I improve my bash script so I don't have to change values all the time?
How can I improve my bash script so I don't have to change values all the time?

Time:06-01

I have this code for bash that exports SVG files to PDF (which is irrelevant right now, really). But I want to change it so I input all the data altogether without needing to comment and uncomment the variables all the time. Thought about creating a loop but not sure how to.

Here is a modified version of it so no sensitive information is involved (see below). Might feel messy (because of Inkscape) but the idea is simple (and independent on Inkscape):

  • I enter the personalized information required for each diploma by school (from which a 2 lists depend on).
  • a for loop creates a different diploma for each value of each list and school.

What I want is to also automatize it so I don't have to change the values for each school all the time, but putting it altogether and let the code exporting so if I have to change anything (eg: the date), I only need to modify one line and compile it once.

I thought about creating functions and a couple of loops, but I'm not sure of how to correctly do it here.

#!/bin/bash

#these should be pretty straightforward
image_width=1028
dpi=600
svg_name=svg
name_placeholder={NOMBRE}
# cole_placeholder={COLE}
dni_placeholder={DNI}
date_placeholder={FECHA}
pdf_base_name=Diploma

#copy the information required here, all enclosed within simple/double quotes 
#and separated by a space

date='31 de mayo de 2022'

# names=('Name 1')
# name_cole='name of the school 1'
# dni=('code 46279W')

# names=('name 2' 'name 3')
# name_cole='name of school 2'
# dni=('code 7911C' 'code 5088D')

names=('name 4' 'name 5')
name_cole='name of school 3'
dni=('code 23182W' 'code 42019N')


#here we're going through all the names, copy the svg for each,
#replace the name and remove the temporary copy of the svg again

#it looks a bit scary, but the most complicated parts are probably the sed command and the inkscape export command.
#this basically tells inkscape to create the pdfs based on the information above.

for i in ${!names[@]}
do
    cp $svg_name.svg temp_copy.svg
    echo ${names[i]// /\\ }
    sed -i s/$name_placeholder/"${names[i]// /\\ }"/ temp_copy.svg
#   sed -i s/$cole_placeholder/"$name_cole"/ temp_copy.svg
    sed -i s/$dni_placeholder/"${dni[i]// /\\ }"/ temp_copy.svg
    sed -i s/$date_placeholder/"$date"/ temp_copy.svg
#   inkscape temp_copy.svg --export-area-page --export-filename=$png_base_name\ ${name_cole// /_}\ ${names[i]// /_}.png --export-width=$image_width --export-dpi=$dpi
    inkscape temp_copy.svg --export-area-page --export-filename=$pdf_base_name\ ${name_cole// /_}\ ${names[i]// /_}.pdf --export-width=$image_width --export-type="pdf"
    rm temp_copy.svg
done
#move all the exported pdfs to a folder, so we don't clutter everything up with them
mkdir pdfs
mkdir pdfs/${name_cole// /_}
mv *pdf pdfs/${name_cole// /_}

CodePudding user response:

Create s script that handles only 1 combination of arguments (school, name, dni):

#! /bin/bash

usage() {
    printf 'usage: %s school name dni\n' "$0"
    exit 1
}

if [[ $# != 3 ]]
then
    usage
fi

school="$1"
name="$2"
dni="$3"

# do whatever you want to do with (school, name, dni), no loop

and then invoke it for all the tuples, like

for school in 'name of school 2' 'name of school 3'
do
    ./myscript "$school" 'name 4' 'code 23182W'
    ./myscript "$school" 'name 5' 'code 42019N'
done

or perhaps taking the values from a file

CodePudding user response:

There are several ways to pass data to a script, and also allow for a default value. For instance, if you define your date in your script by

: ${date:=${1:-31 de mayo de 2022}}

and you call your script by setting the first parameter,

myscript.sh "15 de junho de 1956"

date will be set to this value. However, if you want to call the script often with that same value and don't want to always enter the date on the command line, you can do a

export date="15 de junho de 1956"

and later (in the same process, or a subshell of it), simply write

myscript.sh

and it takes the modified date. If you don't set the environment variable date, it takes the default value specified in the script.

Another possibility, which can be used for easily defining a bundle of parameters in one go, is to use a configuration file. This is sometimes more convenient than defining each parameter separately. In this case you would have at the start of the script something like

. ${xconfig=~/.xconfig}

If you put into the file ~/.xconfig settings such as

date="24 do decembro 2021"

this settings will take effect. Do this only if you are the only person which can modify the configuration file, to avoid malevolent code to be executed by your script. With this setup, you can even prepare several config files and choose the appropriate one when running the script. For example

xconfig=~/etc/my_conf myscript.sh

runs the script, but reads the configuration from ~/etc/my_conf instead of the default location.

CodePudding user response:

You can put the variables in a here document (or an external file, if you like) and then just loop over the values.

This also uses mktemp to create a temporary file name securely, and trap to remove it when we are done.

I have also refactored the code to avoid several common antipatterns. Perhaps try http://shellcheck.net/ for some diagnostics.

tmp=$(mktemp -t svg2pdf.XXXXXXXXXX) || exit
trap 'rm -f "$tmp"' EXIT
while IFS=":" read -r name cole dni date; do
    # write diagnostics to standard error; include name of script
    echo "$0: $name" >&2
    # run sed just once, replaces cp too
    sed -e "s/\$name_placeholder/${name// /\\ }/" \
        -e "#s/\$cole_placeholder/$cole/" \
        -e "s/\$dni_placeholder/${dni// /\\ }/" \
        -e "s/\$date_placeholder/$date/" "$svg_name.svg" >"$tmp"
    inkscape "$tmp" --export-area-page \
        --export-filename="$pdf_base_name\ ${cole// /_}\ ${name// /_}.pdf" \
        --export-width=$image_width --export-type="pdf"
done <<\____DONE
Fred Flintstone:École Supérieure de Bédrocque:code 1234:31 de mayo de 2022
Barney Rubble:Outback Academy of Alice Springs:code 5555:31 de mayo de 2022
____DONE    

I obviously had to guess a bit as to what your values contain and how they would be formatted.

If your data is moderately more complex than this, perhaps think about using a structured format like JSON instead, though tooling for handling it in shell scripts is not properly standardized (probably investigate jq). But from your exposition, it looks like a simple delimited format would work fine here. (As you can see, I used a colon delimiter; depending on your data, comma or semicolon or | could work fine, too. Just use a character which does not occur in your actual data.)

More generally, you want to avoid mixing code and data. If you need to process the same data over and over, embedding it in the script makes sense. If it's just a temporary need, refactoring the script so it reads the variables from an external file or standard input is a better design. You want to use version control on all your program code anyway, and not make any changes you do not immediately check in. (If you are not currently using version control, probably abandon whatever you are doing and tackle that first.)

Here's a quick and dirty demo which reads standard input instead: https://ideone.com/nZq9EO

  • Related