Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a bash command that can tell the size of a shell variable

Tags:

linux

bash

Is there a way to find the size(memory used) of shell variable from command line, without using C ?

like image 281
Ankur Agarwal Avatar asked Feb 14 '11 01:02

Ankur Agarwal


2 Answers

For a scalar variable, ${#VAR} gives you the length in characters. In a unibyte locale, this is the length in bytes. The size in bytes is the length of the name in bytes, plus the length of the value in bytes, plus a constant overhead.

LC_ALL=C
name=VAR
size=$(($#name + $#VAR)) # plus a small overhead

If the variable is exported, the size is roughly double.

LC_ALL=C
name=VAR
size=$((($#name + $#VAR) * 2)) # plus a small overhead

For an array variable, you need to sum up the lengths (again, in bytes) of the elements, and add a constant overhead per element plus a constant overhead for the array.

LC_ALL=C
name=VAR
size=$(($#name)) # plus a small overhead
for key in "${!VAR[@]}"; do
  size=$((size + ${#key} + ${#VAR[$key]})) # plus a small overhead
done

Here's a minimally tested function that computes the approximate size occupied by a variable. Arrays and exports are taken into account, but not special read-only variables such as $RANDOM. The sizes have been observed on bash 4.2, different versions may have different overheads. You may need to adjust the constants depending on your system types and malloc implementation.

_sizeof_pointer=4
_sizeof_int=4
_malloc_granularity=16
_malloc_overhead=16
## Usage: compute_size VAR
## Print the amount of memory (in bytes) used by VAR.
## The extra bytes used by the memory allocator are not taken into account.
add_size () {
  local IFS="+" this extra
  set $(($1 + _malloc_overhead))
  _size=$((_size + $1))
  set $(($1 % _malloc_granularity))
  [[ $1 -eq 0 ]] || _size=$((_size + _malloc_granularity - $1))
}
compute_size () {
  local LC_ALL=C _size=0 _key
  if eval "[ -z \${$1+1} ]"; then echo 0; return; fi
  add_size $((_sizeof_pointer*5 + _sizeof_int*2)) # constant overhead
  add_size ${#1} # the name
  case $(declare -p $1) in
    declare\ -x*)
      eval "add_size \${#$1}" # the value
      eval "add_size \$((\${#1} + \${#$1} + 2))" # the export string
      ;;
    declare\ -a*)
      eval 'for _key in "${!'$1'[@]}"; do
              add_size $_key
              add_size ${#'$1'[$_key]}
              add_size $((_sizeof_pointer*4))
            done'
      add_size $((_sizeof_pointer*2 + _sizeof_int*2))
      add_size $((_sizeof_pointer*4))
      ;;
    *)
      eval "add_size \${#$1}" # the value
      ;;
  esac
  echo $_size
}
like image 127
Gilles 'SO- stop being evil' Avatar answered Nov 09 '22 23:11

Gilles 'SO- stop being evil'


This tells you how many characters are in the value of a scalar variable named "var":

echo ${#var}

This tells you the number of elements in an array named "array":

echo ${#array[@]}

This tells you the number of characters in an element of an array:

echo ${#array[3]}

If you try to get the size of an array and you leave out the [@] index, you get the length of element 0:

$ array=(1 22 333 4444)
$ echo ${#array}
1
$ echo ${#array[@]}
4
$ echo ${#array[2]}
3

If you want the total length of all elements of an array, you could iterate over the array and add them up, you could use IFS and some steps similar to those below, or you could:

$ tmp="${array[*]}"
$ echo $(( ${#tmp} - ${#array[@]} + 1 ))
10

Beware of using the number of elements in an array as the index of the last element since Bash supports sparse arrays:

$ array=(1 22 333 4444 55555)
$ echo ${#array[@]}
5
$ array[9]=999999999
$ echo ${#array[@]}
6
$ echo ${array[${#array[@]} - 1]}    # same as echo ${array[6 - 1]}

$ # only a newline is echoed since element 5 is empty (only if "nounset" option* is not set (default in most cases))
$ # when "nounset" option is set (possibly using command "set -u") then bash will print such error:
$ # bash: array[${#array[@]} - 1]: unbound variable
$ unset "array[1]"    # always quote array elements when you unset them
$ echo ${#array[@]}
5
$ echo ${array[${#array[@]} - 1]}    # same as echo ${array[5 - 1]}
55555

That was obviously not the last element. To get the last element:

$ echo ${array[@]: -1}    # note the space before the minus sign
999999999

Note that in the upcoming Bash 4.2, you can do echo ${array[-1]} to get the last element. In versions prior to 4.2, you get a bad subscript error for negative subscripts.

To get the index of the last element:

$ idx=(${!array[@]})
$ echo ${idx[@]: -1}
9

Then you can do:

$ last=${idx[@]: -1}
$ echo ${array[last]}
999999999

To iterate over a sparse array:

for idx in ${!array[@]}
do
    something_with ${array[idx]}
done

* I recommend avoiding nounset

like image 22
Dennis Williamson Avatar answered Nov 09 '22 23:11

Dennis Williamson