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.

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.)
    – chepner
    Commented Aug 5, 2016 at 17:40
  • 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.
    – shellter
    Commented Aug 5, 2016 at 21:10

2 Answers 2

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.

2
  • declare -n identification throws an error in bash and i looked up the -n option and can't find anything about it? Commented Mar 14, 2018 at 18:51
  • @transformerTroy Do you have Bash 4.3 or newer? That's when it was introduced. Commented Mar 14, 2018 at 19:06
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.

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? Commented Apr 21, 2021 at 21:01
  • 1
    i think it was in this post: stackoverflow.com/questions/52284745/… 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. Commented Apr 23, 2021 at 5:04
  • 1
    sudo apt-get install jq Those who are getting jq: not found Commented Jun 28, 2021 at 6:59
  • What distro and version of linux are you using? Commented Aug 17, 2021 at 7:12
  • d in Done is small. you will get Error : Unexpected End of File otherwise Commented Jun 8, 2023 at 12:13