Creating array of objects in bash
Asked Answered
V

2

20

Is it possible to create an array of objects in bash?

That's how I'm trying:

declare -a identifications=(
  {
    email    = '...',
    password = '...'
  }
)

declare -a years=(
  '2011'
  '2012'
  '2013'
  '2014'
  '2015'
  '2016'
)

for identification in "${identifications[@]}"
do
  for year in "${years[@]}"
  do
    my_program --type=CNPJ --format=XLS --identification=${identification.email} --password=${identication.password} --competence=${year} --output="$identification - $year"
  done
done

Obviously, this doesn't work, and I'm not finding how to achieve that, since I'm not finding bash objects.

Venireman answered 5/8, 2016 at 17:3 Comment(2)
bash has only one data type: string. Even arrays are simply another form of syntactic quoting, to allow lists of strings containing arbitrary values (i.e., whitespace). (Associative arrays, introduced in bash 4, are slightly better, but still no where near sufficient to allow the types of data structures you are looking for.)Gove
ksh93 and later support variables defined as you describe. Unfortunately, ksh seems to be abandon-ware, as even the man-pages pointed to from kornshell.com are now dead links (and have been for a while). AND I wouldn't be able to point you to documentation on how to use your ksh for that feature. (Its probably out there somewhere). Good luck.Nasty
B
45

You could do some trickery with associative arrays (introduced in Bash 4.0) and namerefs (see manual for declare and the first paragraph of Shell Parameters – introduced in Bash 4.3):

#!/usr/bin/env bash

declare -A identification0=(
    [email]='[email protected]'
    [password]='admin123'
)
declare -A identification1=(
    [email]='[email protected]'
    [password]='passwd1!'
)

declare -n identification
for identification in ${!identification@}; do
    echo "Email: ${identification[email]}"
    echo "Password: ${identification[password]}"
done

This prints

Email: [email protected]
Password: admin123
Email: [email protected]
Password: passwd1!

declare -A declares an associative array.

The trick is to assign all your "objects" (associative arrays) variable names starting with the same prefix, like identification. The ${!prefix@} notation expands to all variable names starting with prefix:

$ var1=
$ var2=
$ var3=
$ echo "${!var@}"
var1 var2 var3

Then, to access the key-value pairs of the associative array, we declare the control variable for the for loop with the nameref attribute:

declare -n identification

so that the loop

for identification in ${!identification@}; do

makes identification behave as if it were the actual variable from the expansion of ${!identification@}.

In all likelihood, it'll be easier to do something like the following, though:

emails=('[email protected]' '[email protected]')
passwords=('admin123' 'passwd1!')
for (( i = 0; i < ${#emails[@]}; ++i )); do
    echo "Email: ${emails[i]}"
    echo "Password: ${passwords[i]}"
done

I.e., just loop over two arrays containing your information.

Bikaner answered 5/8, 2016 at 17:47 Comment(2)
declare -n identification throws an error in bash and i looked up the -n option and can't find anything about it?Noncompliance
@Noncompliance Do you have Bash 4.3 or newer? That's when it was introduced.Bikaner
V
6

I tend to use json to create objects. For me it makes it really easy and flexible.

Here is a oversimplified example.

I create a json file: devices.json

{
          "backup" : [{
                "addr":"192.168.1.1",
                "username":"backuper",
                "dstfile":"firewallconfig",
                "ext":".cfg",
                "method":"ssh",
                "rotate":"no",
                "enabled":"yes"
            }, {
                "addr":"192.168.1.2",
                "username":"backuper",
                "dstfile":"routerconfig",
                "ext":".cfg",
                "method":"ssh",
                "rotate":"no",
                "enabled":"yes"
            }]
 }

Bash script: task.sh

 # read the devices.json file and store it in the variable jsonlist
    jsonlist=$(jq -r '.backup' "devices.json")
        # inside the loop, you cant use the fuction _jq() to get values from each object.
        for row in $(echo "${jsonlist}" | jq -r '.[] | @base64'); do
            _jq()
            {
             echo ${row} | base64 --decode | jq -r ${1}
            }
         
        echo "backing up: $(_jq '.addr')"
        echo "using method: $(_jq '.method')"
        done

The guy who posted the original post can be found by googling "using json with bash" or something.

Vet answered 16/1, 2021 at 14:45 Comment(6)
Can you please post the link to the original post? I googled "using json with bash" and did not find it. In particular I am looking for any documentation on the function _jq() (with the underscore). Is _jq() actually a separate function from jq() or is that some bash syntax that I am not familiar with?Coz
i think it was in this post: #52285245 But hes not really explaining much. _jq is just a function we create in the for loop. I find this for loop a little confusing to at first. But its quite good.Drat
sudo apt-get install jq Those who are getting jq: not foundCrudden
What distro and version of linux are you using?Drat
d in Done is small. you will get Error : Unexpected End of File otherwiseDoby
Thank you Saurabh Mhase. I corrected it.Drat

© 2022 - 2024 — McMap. All rights reserved.