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