Ged Kirkham

JavaScript - Python - HTML - CSS

manipulating a json file with bash

published 2020/01/06

introduction

i was tasked with adding the titles of all countries to an i18n language file and as this list was 244 lines long, i did not want to spend my entire day manipulating each individual line. an easy way to automate this is with a bash script.

inputs and outputs

the input file is an object that lists all the countries with their associated iso code. my intention is to have each key set to an i18n variable so that it is compatible with the users local language setting. in order to complete this, i want to modify the input.txt file so that each parameter's key is set to an i18n variable that is specified by the imported i18n.js file. the i18n.js file will contain all of the countries listed in english.

input.txt

const countriesMap = {
'United Kingdom': 'UK',
'United States': 'US',
'Côte d'Ivoire': 'CI'
}

export default as countriesMap

countriesMap.js

import i18n from '@/src/utils/i18n.js

const countriesMap = {
[i18n.t('country.uk')]: 'UK',
[i18n.t('country.us')]: 'US',
[i18n.t('country.ci')]: 'CI'
}

export default as countriesMap

i18n.js

country {
"uk":"United Kingdom",
"us":"United States",
"ci":"Côte d'Ivoire"
}

shell script

the full script below.

initialLine=2
finalLine=4

echo -e "import i18n from '@/src/utils/i18n.js\n" > countriesMap.js
head -n 1 input.txt >> countriesMap.js

echo "country {" > i18n.txt

cat input.txt | sed -n $initialLine','${finalLine}p |
while read line;
do
iso=${line#*:}
cleaned=`echo $iso | sed "s/'//g; s/,//g"`
echo "[i18n.t('country.${cleaned,,}')]:${iso}" >> countriesMap.js

country=${line%:*}
lastChar=`echo "${iso: -1}"`

if [ $lastChar != ',' ]
then
lastChar=''
fi

countryTrimmed=${country:1}
countryTrimmed=${countryTrimmed%?}
echo "\"${cleaned,,}\":\"${countryTrimmed}\"${lastChar}" >> i18n.txt
done

tail -n +$((finalLine + 1)) input.txt >> countriesMap.js

echo "}" >> i18n.txt

for those wanting it to be broken down a bit further.

i specified the first and last line of the input.txt file that i want to modify within the coming while loop - the while loop is where the transformation of the countries and their respective iso code takes place.

initialLine=2
finalLine=4

as i will be using i18n.js variables within countriesMap.js, i will need to import it within the respective file. the below code simply creates a new file titled countriesMap.js if the file does not exist, or if it does exist, empty the contents of the file before writing anything to it. the -e option has been specified as i want to enable the interpretation of \n listed at the end of the string. the echo command already writes each given string with a newline, but i wanted there to be an extra new line to give some visual separation. > is then used to redirect the output of echo to countriesMap.js.

echo -e "import i18n from '@/src/utils/i18n.js\n" > countriesMap.js

the input file contains the object declaration - line 1, and i don't want to pass this to the while loop, but i do want to include it in the countries.map.js. to write this line to countriesMap.js, i can use the head command which will print the first 10 lines if no options are specified. the -n 1 option specifies that from the head of the document, only the first line is read. this is then appended >> to countriesMap.js

head -n 1 input.txt >> countriesMap.js

to loop through the long list of countries, we need to initiate a while loop. the content of input.txt is first passed to sed via the | operator. sed allows us to select a subset of lines that we want to pass to the while loop. this is specified by $initialLine','${finalLine}p - line 2 to 4. the output of sed is then passed to the while loop which iterates through the input line by line.

cat input.txt | sed -n $initialLine','${finalLine}p |
while read line;

due to the above steps, the below three lines will be processed within the while loop.

'United Kingdom': 'UK',
'United States': 'US',
'Côte d'Ivoire': 'CI'

firstly, we want to grab the iso code. this can be done by using a combination of line which is the line we are currently working on, then splitting the string on the : character, and removing everything before - and including - the colon character.

iso=${line#*:}

the iso variable will need some cleaning, so the sed stream editor is employed to remove all single quotes, and commas from the string.

cleaned=`echo $iso | sed "s/'//g; s/,//g"`

cleaned and iso string variables are then ready to be brought together in a string, and passed into the countriesMap.js file. as that is being done, the cleaned string is manipulated to lower case by ,,.

echo "[i18n.t('country.${cleaned,,}')]:${iso}" >> countriesMap.js

we also want to retrieve the country title and this can be achieved to a similar process above. instead of using the # character which removes everything prior and including the specified character, we use % which removes everything after the specified character.

country=${line%:*}

next, we want to grab the last character of the iso variable, and check to see if it a comma. if it a comma, we can determine that it is not the final line in the loop so the comma should be added. if it is not a comma, we know we have reached the final line and no comma needs to be added. this is determined by the if statement.

lastChar=`echo "${iso: -1}"`

if [ $lastChar != ',' ]
then
lastChar=''
fi

the country titles are still surrounded by apostrophes. these can be removed by the below commands. :1 remove the character at index 0. %? returns a string from index 0 to the strings length -1.

countryTrimmed=${country:1}
countryTrimmed=${countryTrimmed%?}

we then concatenate the strings together to form our final string and print that to i18n.txt. this is then repeated for every line.

echo "\"${cleaned,,}\":\"${countryTrimmed}\"${lastChar}" >> i18n.txt
done

the last step is to grab the final line from the input.txt and output it to countriesMap.js. by default, the tail command reads the last 10 lines from a file, but by specifying -n and a line number +$((finalLine + 1)), we can pick the line that we want.

tail -n +$((finalLine + 1)) input.txt >> countriesMap.js