How to initialize an array using awk and bash? - arrays

I am trying to store values of the first line of a text file into an array. Here is what I have so far:
arr_values=()
awk '
NR==1 {
for (i=0; i<=NF; i++)
'arr_values[i]'=$i
}' file.txt
for ((i=0; i<${#arr_values[#]}; i++))
do
echo arr_values[i]
done
I am getting an error with initializing the array mainly because I don't know how to use awk to initialize an external array. Any suggestions (only with awk)? Thanks.

You can do this:
read -a array <<< $(head -n 1 file)
echo ${array[0]}
echo ${array[1]}

You probably can simply just do
read -ra arr_values < file.txt
Which would only process the first line and split it uniformly like awk does; saving it into arr_values. No need to fork with external binary commands.

Related

How to write only necessary lines from a file into an array in bash script?

I wrote the following code to read a file and write it in a list using bash script:
index=0
while read line
do
array[$index]="$line"
index=$(($index+1))
done < ../../file.xml
However, I only need to write lines into the array if they contain the word "icon". An array element should look like this:
<icon height="36" width="36" density="ldpi" src="res/icon/android/ldpi.png"/>
Could anyone help me to fix this problem?
Trivially, with a condition.
case $line in *icon*) ... do stuff;;
You should probably fix the syntax to use read -r and the index variable is really unnecessary.
array=()
while read -r line
do
case $line in *icon*) array+=("$line");; esac
done < ../../file.xml
More sensibly, do it all in one fell swoop, in Bash 4+
readarray index < <(grep 'icon' ../../file.xml)
Probably most sensibly, if the file really is XML, use an XML parser like xmlstarlet to properly identify and extract the structure you want to examine.
readarray index < <(xmlstarlet sel -t -m //icon -c . -n ../../file.xml)
You can use regex:
regex="icon"
index=0
while read line
do
if [[ $line =~ $regex ]]; then
array[$index]="$line"
#If you need cut address uncommend next line and comment before line
#array[$index]="$(sed 's/.*src=\"\(.*\)\".*/\1/' <<< $line)"
index=$(($index+1))
fi
done < ../../file.xml

Bash, wihle read line by line, split strings on line divided by ",", store to array

I need to read file line by line, and every line split by ",", and store to array.
File source_file.
usl-coop,/root
usl-dev,/bin
Script.
i=1
while read -r line; do
IFS="," read -ra para_$i <<< $line
echo ${para_$i[#]}
((i++))
done < source_file
Expected output.
para_1[0]=usl-coop
para_1[1]=/root
para_2[0]=usl-dev
para_2[1]=/bin
Script will out error about echo.
./sofimon.sh: line 21: ${para_$i[#]}: bad substitution
When I echo array one by one field, for example
echo para_1[0]
it shows, that variables are stored.
But I need use it with variable within, something like this.
${para_$i[1]}
Is possible to do this?
Thanks.
S.
There is a trick to simulate 2D arrays using associative arrays. It works nice and I think is the most flexible and extensible:
declare -A para
i=1
while IFS=, read -r -a line; do
for j in ${!line[#]}; do
para[$i,$j]="${line[$j]}"
done
((i++)) ||:
done < source_file
declare -p para
will output:
declare -A para=([1,0]="usl-coop" [1,1]="/root" [2,1]="/bin" [2,0]="usl-dev" )
Without modifying your script that much you could use indirect variable expansion. It's sometimes used in simpler scripts:
i=1
while IFS="," read -r -a para_$i; do
n="para_$i[#]"
echo "${!n}"
((i++)) ||:
done < source_file
declare -p ${!para_*}
or basically the same with a nameref a named reference to another variable (side note: see how [#] needs to be part of the variable in indirect expansion, but not in named reference):
i=1
while IFS="," read -r -a para_$i; do
declare -n n
n="para_$i"
echo "${n[#]}"
((i++)) ||:
done < source_file
declare -p ${!para_*}
both scripts above will output the same:
usl-coop /root
usl-dev /bin
declare -a para_1=([0]="usl-coop" [1]="/root")
declare -a para_2=([0]="usl-dev" [1]="/bin")
That said, I think you shouldn't read your file into memory at all. It's just a bad design. Shell and bash is build around passing your files with pipes, streams, fifos, redirections, process substitutions, etc. without ever saving/copying/storing the file. If you have a file to parse, you should stream it to another process, parse and save the result, without ever storing the whole input in memory. If you want some data to find inside a file, use grep or awk.
Here is a short awk script that do the task.
awk 'BEGIN{FS=",";of="para_%d[%d]=%s\n"}{printf(of, NR, 0, $1);printf(of, NR, 1, $2)}' input.txt
Provide the desired output.
Explanation:
BEGIN{
FS=","; # set field seperator to `,`
of="para_%d[%d]=%s\n" # define common printf output format
}
{ # for each input line
printf(of, NR, 0, $1); # output for current line, [0], left field
printf(of, NR, 1, $2) # output for current line, [1], right field
}

Getting the output of a cat command into an array

How can I get just the filenames into an array using the cat command?
How I've been trying:
array=()
while IFS= read -r -d $'\0'; do
array+=("$REPLY")
done < <(cat /proc/swaps | grep "swap")
This either grabs all the information from the output into an array, or just doesn't work. How can I successfully get my expected output of [/swapfile, /dev/hda1, /some/other/swap] into an array form using the cat command?
readarray array < <(awk '/swap/{print $1}' /proc/swaps)
Bash introduced readarray in version 4 which can take the place of the while read loop. readarray is the solution you want.
here is the syntax
readarray variable < inputfile
echo "${variable[0]}" ' to print the first element in array

How can I assign a value to an array in Bash?

I am trying to read a list of values from a text file, hello.txt, and store them in an array.
counter=0
cat hello.txt | while read line; do
${Unix_Array[${counter}]}=$line;
let counter=counter+1;
echo $counter;
done
echo ${Unix_Array[0]}
echo ${Unix_Array[1]}
echo ${Unix_Array[2]}
I am not able to assign values to the array Unix_Array[]... The echo statement does not print the contents of the array.
There are a few syntax errors here, but the clear problem is that the assignments are happening, but you're in an implied subshell. By using a pipe, you've created a subshell for the entire while statement. When the while statement is done, the subshell exits and your Unix_Array ceases to exist.
In this case, the simplest fix is not to use a pipe:
counter=0
while read line; do
Unix_Array[$counter]=$line;
let counter=counter+1;
echo $counter;
done < hello.txt
echo ${Unix_Array[0]}
echo ${Unix_Array[1]}
echo ${Unix_Array[2]}
By the way, you don't really need the counter. An easier way to write this might be:
$ oIFS="$IFS" # Save the old input field separator
$ IFS=$'\n' # Set the IFS to a newline
$ some_array=($(<hello.txt)) # Splitting on newlines, assign the entire file to an array
$ echo "${some_array[2]}" # Get the third element of the array
c
$ echo "${#some_array[#]}" # Get the length of the array
4
If you are using Bash v4 or higher, you can use mapfile to accomplish this:
mapfile -t Unix_Array < hello.txt
Otherwise, this should work:
while read -r line; do
Unix_Array+=("$line")
done < hello.txt
The best way I found is:
declare -a JUPYTER_VENV
JUPYTER_VENV+=( "test1" "test2" "test3" )
And then consume it with:
for jupenv in "${JUPYTER_ENV[#]}"
do
echo "$jupenv"
done
Instead of this:
cat hello.txt | while read line; do
${Unix_Array[${counter}]}=$line;
let counter=counter+1;
echo $counter;
done
You can just do this:
Unix_Array=( `cat "hello.txt" `)
It’s a solution:
count=0
Unix_Array=($(cat hello.txt))
array_size=$(cat hello.txt | wc -l)
for ((count=0; count < array_size; count++))
do
echo ${Unix_Array[$count]}
done

populate and read an array with a list of filenames

Trivial question.
#!/bin/bash
if test -z "$1"
then
echo "No args!"
exit
fi
for newname in $(cat $1); do
echo $newname
done
I want to replace that echo inside the loop with array population code.
Then, after the loop ends, I want to read the array again and echo the contents.
Thanks.
If the file, as your code shows, has a set of files, each in one line, you can assign the value to the array as follows:
array=(`cat $1`)
After that, to process every element you can do something like:
for i in ${array[#]} ; do echo "file = $i" ; done
declare -a files
while IFS= read -r
do
files+=("$REPLY") # Array append
done < "$1"
echo "${files[*]}" # Print entire array separated by spaces
cat is not needed for this.
#!/bin/bash
files=( )
for f in $(cat $1); do
files[${#files[*]}]=$f
done
for f in ${files[#]}; do
echo "file = $f"
done

Resources