Convert for loop results into array in bash - bash

I have a table with column called "names". Can i store each name into different variable?
Right now I am using a for loop and storing the results into single variable.
for j in `psql database_name -U admin -c "select names from table;"`
; do
done
When i do echo$j it prints below result
tom
Harry
steve
How do i store each values(names) in different variables?

Creating one variable per value is impractical. Think about it: How would you access these variables? What you want is an array.
You can define the array as follows:
mapfile -t names < <(psql database_name -U admin -c 'select names from table;')
This will add every line printed by psql as an entry of the array names. To access the entries use ${names[0]}, ${names[1]}, and so on. The size of the array is ${#names[#]}.
Since the question is called »Convert for loop results into array in bash« here's another way to build up the array. However, I wouldn't use a for ... in $(...) loop here as names with spaces will be split and symbols like * will expand.
names=()
for name in $(psql database_name -U admin -c 'select names from table;'); do
names+=("$name")
done
If you want the loop to run some additional commands use the first approach in this answer and loop over the array – this is safer:
mapfile ... # see above
for name in "${names[#]}"; do
# do something with $name
done

Related

Creating a list of variables and calling them later bash

I want to create a list of variables,so later i can do a prompts for users and if their input is matching any variable within that list, I want to use that variable to execute it within a command, for example:
var1=a
var2=b
...
read input
(user chooses var1) command $var1,rest of the command
The biggest problem is that this list will be huge, what would be the best solution? Thanks!
You are looking for the associative array feature in bash 4 or later.
declare -A foo
foo[us]="United States"
foo[uk]="Great Britain"
read -p "Region? " region
echo "${foo[$region]}"
If the value of $region is not a defined key, then ${foo[$region]} will be treated like any unset variable.

Using a variable for associative array key in Bash

I'm trying to create associative arrays based on variables. So below is a super simplified version of what I'm trying to do (the ls command is not really what I want, just used here for illustrative purposes)...
I have a statically defined array (text-a,text-b). I then want to iterate through that array, and create associative arrays with those names and _AA appended to them (so associative arrays called text-a_AA and text-b_AA).
I don't really need the _AA appended, but was thinking it might be
necessary to avoid duplicate names since $NAME is already being used
in the loop.
I will need those defined and will be referencing them in later parts of the script, and not just within the for loop seen below where I'm trying to define them... I want to later, for example, be able to reference text-a_AA[NUM] (again, using variables for the text-a_AA part). Clearly what I have below doesn't work... and from what I can tell, I need to be using namerefs? I've tried to get the syntax right, and just can't seem to figure it out... any help would be greatly appreciated!
#!/usr/bin/env bash
NAMES=('text-a' 'text-b')
for NAME in "${NAMES[#]}"
do
NAME_AA="${NAME}_AA"
$NAME_AA[NUM]=$(cat $NAME | wc -l)
done
for NAME in "${NAMES[#]}"
do
echo "max: ${$NAME_AA[NUM]}"
done
You may want to use "NUM" as the name of the associative array and file name as the key. Then you can rewrite your code as:
NUM[${NAME}_AA]=$(wc -l < "$NAME")
Then rephrase your loop as:
for NAME in "${NAMES[#]}"
do
echo "max: ${NUM[${NAME}_AA]}"
done
Check your script at shellcheck.net
As an aside: all uppercase is not a good practice for naming normal shell variables. You may want to take a look at:
Correct Bash and shell script variable capitalization

Iterating through a result set in shell

So I've searched quite a bit for this and I'm pretty new to Shell,
I want to iterate over Resultset rows in SHELL script and for each row I want to execute some code using each column of the current row.
Lets assume the resultset look like this.
Query Priority Size
---------------------------------------------------
this is a sentence to execute high 124400
this is another example low 15000000
...
So how do I manage to iterate over this Resultset and storing each column into his own variable? Here is an exemple for the first line:
var1="this is a sentence to execute"
var2="high"
var3=124400
#repeat process for next line
Here is what you could do:
Extract your data into a delimited file (say, csv), if possible, avoid having headers. If you do have headers, make sure to exclude them while reading.
Read data from the file into an array or a set of variables
like so:
# using an array - works great if we have a variable number of columns
while IFS=, read -r -a row; do
# ${row[0]} => column1, ${row[1]} => column2 and so on
# process data
done < input.dat
# using variables - works great if we have a fixed set of variables
while IFS=, read -r column1 column2 column3; do
# process data
done < input.dat
See also: How do I split a string on a delimiter in Bash?

Open file in bash script

I've got a bash script accepting several files as input which are mixed with various script's options, for example:
bristat -p log1.log -m lo2.log log3.log -u
I created an array where i save all the index where i can find files in the script's call, so in this case it would be an arrat of 3 elements where
arr_pos[0] = 2
arr_pos[1] = 4
arr_pos[3] = 5
Later in the script I must call "head" and "grep" in those files and i tried this way
head -n 1 ${arr_pos[0]}
but i get this error non runtime
head: cannot open `2' for reading: No such file or directory
I tried various parenthesis combinations, but I can't find which one is correct.
The problem here is that ${arr_pos[0]} stores the index in which you have the file name, not the file name itself -- so you can't simply head it. The array storing your arguments is given by $#.
A possible way to access the data you want is:
#! /bin/bash
declare -a arr_pos=(2 4 5)
echo ${#:${arr_pos[0]}:1}
Output:
log1.log
The expansion ${#:${arr_pos[0]}:1} means you're taking the values ranging from the index ${arr_pos[0]} in the array $#, to the element of index ${arr_pos[0]} + 1 in the same array $#.
Another way to do so, as pointed by #flaschenpost, is to eval the index preceded by $, so that you'd be accessing the array of arguments. Although it works very well, it may be risky depending on who is going to run your script -- as they may add commands in the argument line.
Anyway, you may should try to loop through the entire array of arguments by the beginning of the script, hashing the values you find, so that you won't be in trouble while trying to fetch each value later. You may loop, using a for + case ... esac, and store the values in associative arrays.
I think eval is what you need.
#!/bin/bash
arr_pos[0]=2;
arr_pos[1]=4;
arr_pos[2]=5;
eval "cat \$${arr_pos[1]}"
For me that works.

Bash script execute shell command with Bash variable as argument

I have one loop that creates a group of variables like DISK1, DISK2... where the number at the end of the variable name gets created by the loop and then loaded with a path to a device name. Now I want to use those variables in another loop to execute a shell command, but the variable doesn't give its contents to the shell command.
for (( counter=1 ; counter<=devcount ; counter++))
do
TEMP="\$DISK$counter"
# $TEMP should hold the variable name of the disk, which holds the device name
# TEMP was only for testing, but still has same problem as $DISK$counter
eval echo $TEMP #This echos correctly
STATD$counter=$(eval "smartctl -H -l error \$DISK$counter" | grep -v "5.41" | grep -v "Joe")
eval echo \$STATD$counter
done
Don't use eval ever, except maybe if there is no other way AND you really know what you are doing.
The STATD$counter=$(...) should give an error. That's not a valid assignment because the string "STATD$counter" is not a valid variable name. What will happen is (using a concrete example, if counter happened to be 3 and your pipeline in the $( ) output "output", bash will only expand that line as far as "STATD3=output" so it will try to find a command named "STATD3=output" and run it. Odds are this is not what you intended.
It sounds like everything you want to do can be accomplished with arrays instead. If you are not familiar with bash arrays take a look at Greg's Wiki, in particular this page or the bash man page to find out how to use them.
For example, in the loop you didn't post in your question: make disk (not DISK: don't use all upper case variable names) an array like so
disk+=( "new value" )
or even
disk[counter]="new value"
Then in the loop in your question, you can make statd an array as well and assign it with values from disk by
statd[counter]="... ${disk[counter]} ..."
It's worth saying again: avoid using eval.

Resources