Storing data in multiple arrays (bash) - arrays

I am trying to store the contents of a .txt file in two sets of arrays in bash. The file is a list of characteristics for given data files, delimited by vertical bars (|). So far, I have written code that reads the file and prints each line of data separately, each followed by the given sections of the line.
#prints line of text and then separated version
while IFS='' read -r line || [[ -n "$line" ]]
do
echo "Text read from file: $line"
words=$(echo $line | tr "|" "\n")
for tests in $words
do
echo "> $tests"
done
done < "$1"
Example output:
Text read from file: this|is|data|in|a|file
> this
> is
> data
> in
> a
> file
Text read from file: another|example|of|data
> another
> example
> of
> data
Is there a way for me to store each individual line of data in one array, and then the broken up parts of it within another? I was thinking this might be possible using a loop, but I am confused by arrays using bash (newbie).

OK -- I just read in the lines like you have done, and append them to the lines array. Then, use tr as you have done, and append to the words array. Just use the parentheses to mark them as array elements in the assignments:
$ cat data.txt
this|is|data|in|a|file
another|example|of|data
$ cat read_data.sh
#!/bin/bash
declare -a lines
declare -a words
while IFS='' read -r line || [[ -n "$line" ]]
do
echo "Text read from file: $line"
lines+=( $line )
words+=( $(echo $line | tr "|" " ") )
done < "$1"
for (( ii=0; ii<${#lines[#]}; ii++ )); do
echo "Line $ii ${lines[ii]}"
done
for (( ii=0; ii<${#words[#]}; ii++ )); do
echo "Word $ii ${words[ii]}"
done
$ $ ./read_data.sh data.txt
Text read from file: this|is|data|in|a|file
Text read from file: another|example|of|data
Line 0 this|is|data|in|a|file
Line 1 another|example|of|data
Word 0 this
Word 1 is
Word 2 data
Word 3 in
Word 4 a
Word 5 file
Word 6 another
Word 7 example
Word 8 of
Word 9 data

Related

Read lines looping over array which contains filenames in bash

In bash, I would like to loop over a previously defined array, which contains filenames. In turn, each file of the array must be readed and processed dynamically (while read line...).
This is an example of what the files of the array contains:
_VALUE1_,_VALUE1_,1,Name 1
_VALUE2_,_VALUE2_,1,Name 2
_VALUE3_,_VALUE3_,1,Name 3
_VALUE4_,_VALUE4_,1,Name 4
_VALUE5_,_VALUE5_,1,Name 5
This is what I've tested with no luck.
#!/bin/bash
. functions.sh
GEN_ARQ_ARRAY=("./cfg_file.txt" "./euro_file.txt" "./zl_file.txt")
WB_ARQ_ARRAY=("./rn_cfg_wb_file.txt" "./rn_eur_wb_file.txt" "./rn_zl_wb_file.txt")
BN_ARQ_ARRAY=("./rn_cfg_bn_file.txt" "./rn_eur_bn_file.txt" "./rn_zl_bn_file.txt")
AM_ARQ_ARRAY=("./rn_cfg_am_file.txt" "./rn_eur_am_file.txt" "./rn_zl_am_file.txt")
STATUS_BOOL=true
for i in "${!GEN_ARQ_ARRAY[#]}"; do
while IFS=$'\r' read -r line || [[ -n "$line" ]];do
STACK_NAME=${line%%,*} # Gets the first substring of a string divided by ','
STACK_STATUS=$(curl -su "${USERNAME}":"${PASSWORD}" -X GET http://"${SERVER_NAME}":9100/api/stacks/"${STACK_NAME}"/state | ./jq-linux64 -cr '.result.value')
if [[ $(echo "$STACK_STATUS" | tr -d '\r') == "$STATUS_BOOL" ]]; then
echo "${line}" >> "${GEN_ARQ_ARRAY[i]}"
case ${line} in
*"ARQBS"*|*"ARCBS"*|*"ARQWB"*|*"ARCWB"*) echo "${line}" >> "${WB_ARQ_ARRAY[$i]}";;
*"ARQOF"*|*"ARCOF"*|*"ARQBN"*|*"ARCBN"*) echo "${line}" >> "${BN_ARQ_ARRAY[$i]}";;
*"ARQAM"*|*"ARCAM"*) echo "${line}" >> "${AM_ARQ_ARRAY[$i]}";;
*) echo "$(logWarn) No matches -- ${STACK_NAME}" | tee -a "$LOGFILE";;
esac
else
echo "$(logInfo) ${STACK_NAME} is not running" | tee -a "$LOGFILE"
fi
done < "${GEN_ARQ_ARRAY[i]}"
done
Problem here is that the for loop starts, detects array content, gets the first value of the array, enter into while, and it constantly loops in the first position of the array even with the end of the file is reached. I can't find the way to exit the while loop and continue with the next array position.
I'm pretty sure there is a better way to implement this.
Hearing your ideas!
Edit:
Solved by replacing the line echo "${line}" >> "${GEN_ARQ_ARRAY[i]}", which was in-loop filling up the file.
Solved by replacing the line echo "${line}" >> "${GEN_ARQ_ARRAY[i]}", which was in-loop filling up the file. Once I did, the code worked flawlessly.

Split two numbers in two arrays

I need to split 2 numbers in the form(they are from a text file):
Num1:Num2
Num3:Num4
And store num1 into array X and number 2 in array Y num 3 in array X and num4 in array Y.
With bash:
mapfile -t X < <(cut -d : -f 1 file) # read only first column
mapfile -t Y < <(cut -d : -f 2 file) # read only second column
declare -p X Y
Output:
declare -a X='([0]="num1" [1]="num3")'
declare -a Y='([0]="num2" [1]="num4")'
Disadvantage: The file is read twice.
You could perform the following steps:
Create destination arrays empty
Read file line by line, with a classic while read ... < file loop
Split each line on :, again using read
Append values to arrays
For example:
arr_x=()
arr_y=()
while IFS= read line || [ -n "$line" ]; do
IFS=: read x y <<< "$line"
arr_x+=("$x")
arr_y+=("$y")
done < data.txt
echo "content of arr_x:"
for v in "${arr_x[#]}"; do
echo "$v"
done
echo "content of arr_y:"
for v in "${arr_y[#]}"; do
echo "$v"
done
Here is a quick bash solution:
c=0
while IFS=: read a b ;do
x[$c]="$a"
y[$c]="$b"
c=$((c+1))
done < input.txt
We send the input.txt to a while loop, using Input Field Separator : and read the first number of each line as $a and second number as $b. Then we add them to the array as you specified. We use a counter $c to iterate the location in the arrays.
Using =~ operator to store the pair of numbers to array $BASH_REMATCH:
$ cat file
123:456
789:012
$ while read -r line
do
[[ $line =~ ([^:]*):(.*) ]] && echo ${BASH_REMATCH[1]} ${BASH_REMATCH[2]}
# do something else with numbers as they will be replaced on the next iteration
done < file

Bash - Looping through the content of file and perform actions on rows and columns

The structure of the my input files are as follows:
<string1> <string2> <stringN>
hello nice world
one three
NOTE:, the second row has a tab/null on the second column. so second column on second row is empty and not 'three'
In bash, I want to loop through each row and also be able to process each individual column (string[1-N])
I am able to iterate to each row:
#!/bin/bash
while IFS='' read -r line || [[ -n "$line" ]]; do
line=${line/$/'\t'/,}
read -r -a columns <<< "$line"
echo "current Row: $line"
echo "column[1]: '${columns[1]}'"
#echo "column[N] '${columns[N]}'"
done < "${1}"
Expected result:
current Row: hello,nice,world
column[1]: 'nice'
current Row: one,,three
column[1]: ''
Basically what I do is iterate through the input file (here passed as argument), do all the "cleaning" like prevents whitespace from being trimmed, ignore backslashes an consider also the last line.
then I replace the tabs '\t' by a comma
and finally read the line into an array (columns) to be able to select a particular column.
The input file has tabs as separator value, so I tried to convert it to csv format, I am not sure if the regex I use is correct in bash, or something else is wrong because this does not return a value in the array.
Thanks
You are almost there, a little fix on the on translating '\t' to commas and you have to set also IFS to be the comma.
try this:
#!/bin/bash
while IFS='' read -r line || [[ -n "$line" ]]; do
line=${line//$'\t'/,}
IFS=',' read -r -a columns <<< "$line"
#echo "current Row: $line"
echo "column[0]:'${columns[0]}' column[1]:'${columns[1]}' column[2]:'${columns[2]}'"
done < "${1}"
run:
$> <the_script> <the_file>
Outputs:
column[0]:'hello' column[1]:'nice' column[2]:'world '
column[0]:'one' column[1]:'' column[2]:'three'

Read a file and affect the splitting result in an array with bash

My code read a file by line and split each line by a comma ; or space and the results is affected to an array, but the proble is that i can't read the elmen of the array
#!/bin/bash
filename="$1"
while read -r line
do
name=$line
echo "Name read from file - $name"
arr=$(echo $name | tr ";" "\n")
echo ${arr[1]}
for x in $arr
do
echo "> [$x]"
var1=$x
var2=$x
done
done < "$filename"
the problem is in the command:
echo ${arr[1]}
the file that i use contain line :
car; vehicle
computer;apple
To loop through an array:
for x in "${arr[#]}"; do
echo "> [$x]"
done
The ${arr[#]} expansion will include the entire array, while $arr alone only includes the first array element.
However if you use read -ra with custom IFS then you can directly read each delimited line into an array:
while IFS=';' read -ra arr; do
printf "[%s]\n" "${arr[#]}"
echo '----------'
done < file
Output:
[car]
[vehicle]
----------
[computer]
[apple]
----------

Is there a way to search an entire array inside of an argument?

Posted my code below, wondering if I can search one array for a match... or if theres a way I can search a unix file inside of an argument.
#!/bin/bash
# store words in file
cat $1 | ispell -l > file
# move words in file into array
array=($(< file))
# remove temp file
rm file
# move already checked words into array
checked=($(< .spelled))
# print out words & ask for corrections
for ((i=0; i<${#array[#]}; i++ ))
do
if [[ ! ${array[i]} = ${checked[#]} ]]; then
read -p "' ${array[i]} ' is mispelled. Press "Enter" to keep
this spelling, or type a correction here: " input
if [[ ! $input = "" ]]; then
correction[i]=$input
else
echo ${array[i]} >> .spelled
fi
fi
done
echo "MISPELLED: CORRECTIONS:"
for ((i=0; i<${#correction[#]}; i++ ))
do
echo ${array[i]} ${correction[i]}
done
otherwise, i would need to write a for loop to check each array indice, and then somehow make a decision statement whether to go through the loop and print/take input
The ususal shell incantation to do this is:
cat $1 | ispell -l |while read -r ln
do
read -p "$ln is misspelled. Enter correction" corrected
if [ ! x$corrected = x ] ; then
ln=$corrected
fi
echo $ln
done >correctedwords.txt
The while;do;done is kind of like a function and you can pipe data into and out of it.
P.S. I didn't test the above code so there may be syntax errors

Resources