I am parsing a log file and creating associative arrays for each user with the line number and the last field (total time logged in). The lines of the log file look like this:
jww3321 pts/2 cpe-76-180-64-22 Mon Oct 18 23:29 - 00:27 (00:58)
jpd8635 pts/1 cpe-74-67-131-24 Mon Oct 18 23:22 - 03:49 (04:26)
Where the first field (jww3321) will be the array name and the first entry in the array will be (1,00:58), the next will be (2,(the next time for user)). In order to obtain the proper keys I need to save the the length of list and add one to it when I add the next value to a user array. My code so far looks like this:
cat lastinfo.txt | while read line
do
uname=`echo "$line" | awk '{print $1;}'`
count=`echo "${#$uname[@]}"`
echo "$count"
done
I have tried using indirect references but I am stuck with this error:
l8t1: line 7: ${#$uname[@]}: bad substitution
Any suggestions?
Instead of having so many similar variables, you should use an array, or in your case an array of arrays: var myVariableArray = new double[][] { c[0], c[1], ... }; Now you can easily acces the i-th number within that array: double a = myVariableArray[i][i];
An array is a variable containing multiple values. Any variable may be used as an array. There is no maximum limit to the size of an array, nor any requirement that member variables be indexed or assigned contiguously.
How to Echo a Bash Array? To echo an array, use the format echo ${Array[0]}. Array is your array name, and 0 is the index or the key if you are echoing an associative array. You can also use @ or * symbols instead of an index to print the entire array.
I'm not sure if I understood correctly what you are trying to do, specifically the "associative" part (I can't see where an associative array is used), but this code does what I UNDERSTAND that you want to do:
#!/bin/bash
while IFS=" " read user time; do
eval "item=\${#$user[@]} ; $user[\$item]=\(\$((\$item + 1)),$time\)"
[[ "${arraynames[@]}" =~ $user ]] || arraynames[${#arraynames[@]}]=$user
done< <(sed -r 's/^ *([[:alnum:]]*) .*\((.*)\)$/\1 \2/')
for arrayname in ${arraynames[@]}; do
eval "array=(\${$arrayname[@]})"
echo "$arrayname has ${#array[@]} entries:"
for item in ${!array[@]}; do
echo "$arrayname[$item] = ${array[$item]}"
done
echo
done
It reads from stdin. I've tested it with an example file like this:
jww3321 pts/2 cpe-76-180-64-22 Mon Oct 18 23:29 - 00:27 (00:58) jpd8635 pts/1 cpe-74-67-131-24 Mon Oct 18 23:22 - 03:49 (04:26) jww3321 pts/2 cpe-76-180-64-22 Mon Oct 18 23:29 - 00:27 (01:58) jpd8635 pts/1 cpe-74-67-131-24 Mon Oct 18 23:22 - 03:49 (05:26)
Output:
jww3321 has 2 entries: jww3321[0] = (1,00:58) jww3321[1] = (2,01:58) jpd8635 has 2 entries: jpd8635[0] = (1,04:26) jpd8635[1] = (2,05:26)
Note that only standard integer-indexed arrays are used. In Bash, as of now, indirect array references in the left side always involve using eval
(uuuuuuhhhh, ghostly sound), in the right side you can get away with ${!}
substitution and command substitution $()
.
Rule of thumb with eval: escape what you want to be expanded at eval
time, and don't escape what you want to be expanded before eval
time. Any time you're in doubt about what ends up being eval'd, make a copy of the line and change eval
for echo
.
edit: to answer sarnold's comment, a way to do this without eval:
#!/bin/bash
while IFS=" " read user time; do
array=$user[@] array=( ${!array} ) item=${#array[@]}
read $user[$item] <<< "\($(($item + 1)),$time\)"
[[ "${arraynames[@]}" =~ $user ]] || arraynames[${#arraynames[@]}]=$user
done< <(sed -r 's/^ *([[:alnum:]]*) .*\((.*)\)$/\1 \2/')
for arrayname in ${arraynames[@]}; do
array=$arrayname[@] array=( ${!array} )
echo "$arrayname has ${#array[@]} entries:"
for item in ${!array[@]}; do
echo "$arrayname[$item] = ${array[$item]}"
done
echo
done
You are not creating associative arrays. The error is related to the syntax of ${#$uname[@]}
: delete the second dollar sign.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With