This question already has answers here:
A variable modified inside a while loop is not remembered
(8 answers)
Closed 5 years ago.
I am writing a Bash Shell script, which reads a bunch of lines from a file, and tries to append each line to an array, thus:
# counter variable
i=0
cat doc.txt | while read -r line; do
myArr[i]="$line"
((i=i+1))
done
The file doc.txt contains one word in every line. When I print the array (through echo ${myArr[#]} or for x in ${myArr[#]};do echo $x; done), my array seems to be empty. What am I doing wrong? Thanks!
Edit: I also tried doing this in Ksh, with the same results. An answer in either Bash or Ksh is acceptable, since I seem to be having the same issue in both.
You are running the while loop in a subshell, so any changes you make to the array disappear once that shell exits. Use input redirection instead:
while IFS= read -r line; do
myArr+=("$line")
done < doc.txt
If you using bash 4.0 or later, you can use a single command
readArray -t myArr < doc.txt
Related
This question already has answers here:
Creating an array from a text file in Bash
(7 answers)
Closed 1 year ago.
This is how the script gets started over the terminal:
echo 'username1 username2 username3' | ./security_check.sh
This is the part of the program which should read the stdin and put it into an array:
while read -r line_input
do
i_users[i]=$line_input
i+=1
done
IFS is not set to something different in this script.
After the input is saved in an array it should be able to get printed like this:
for i in ${i_users[#]}
do
echo "$i"
done
This script takes the whole input through stdin and puts it just in i_users[0].
How can I fix this? The array i_users was declared with declare -a i_users at the beginning of the script
Consider:
while read -r line_input is going to read data one line at a time
the script has one line of input (username1 username2 username3)
so the array ends up with a single entry (index=0 / value=username1 username2 username3)
To break the single line of input into separate array entries you have a few options, one being the read -a suggested by #choroba:
$ cat security_check.sh
#!/bin/bash
read -ra i_users
typeset -p i_users
Running OP's example script call:
$ echo 'username1 username2 username3' | ./security_check.sh
declare -a i_users=([0]="username1" [1]="username2" [2]="username3")
NOTE: This works for a single line of input (as shown in OP's sample script call).
This question already has answers here:
bash command not found when setting a variable
(2 answers)
Closed 2 years ago.
Bash in Linux give me command not found error in Line 9 where I do $array[$i]="Name_Of_File$i".
There are also the correct numbers printed besides the error. But i can't figure how it is possible. Maybe some spaces ? On the net i fuond these sintax of writing assignement with an array.
declare -a array;
start=0;
NumFile=$(ls -1 -d log/log_cassiere* | wc -l);
for (( i=$start; i<$NumFile; i++))
do
$array[$i]="Name_Of_File$i";
done
echo ${array[0]};
The problem with:
$array[$i]="Name_Of_File$i"
is that it is actually interpreting $array[$i] because you have a $ preceding it. That means it will attempt to replace the left side of the assignment with the value. Instead, you should have:
array[$i]="Name_Of_File$i"
This question already has answers here:
Why does my Bash counter reset after while loop
(3 answers)
Closed 6 years ago.
I have this Bash snippet:
SourceFiles=()
grep --no-filename -R '^[a-zA-Z]' "$SourceDirectory/XilinxCoreLib/vhdl_analyze_order" | while read File; do
SourceFiles+=("$SourceDirectory/XilinxCoreLib/$File")
done
echo $SourceFiles
It reads a file from disk and pipes all lines that start with an alphabetical character to the while loop.
My problem is that $SourceFiles is empty after processing the loop. I added an echo $File into the loop, which prints all lines from the file. So I assume a variable scoping problem.
What should I change here to get it working?
Each element of a pipeline is run in its own subshell, meaning its own execution environment; so to fix this, you'll need to move your while-loop out of a pipeline.
The simplest fix is to move the grep command into a process substitution; that is, change this structure:
grep ... | while ...
...
done
to this:
while ...
...
done < <(grep ...)
I'm trying to read the lines of output from a subshell into an array, and I'm not willing to set IFS because it's global. I don't want one part of the script to affect the following parts, because that's poor practice and I refuse to do it. Reverting IFS after the command is not an option because it's too much trouble to keep the reversion in the right place after editing the script. How can I explain to bash that I want each array element to contain an entire line, without having to set any global variables that will destroy future commands?
Here's an example showing the unwanted stickiness of IFS:
lines=($(egrep "^-o" speccmds.cmd))
echo "${#lines[#]} lines without IFS"
IFS=$'\r\n' lines=($(egrep "^-o" speccmds.cmd))
echo "${#lines[#]} lines with IFS"
lines=($(egrep "^-o" speccmds.cmd))
echo "${#lines[#]} lines without IFS?"
The output is:
42 lines without IFS
6 lines with IFS
6 lines without IFS?
This question is probably based on a misconception.
IFS=foo read does not change IFS outside of the read operation itself.
Thus, this would have side effects, and should be avoided:
IFS=
declare -a array
while read -r; do
array+=( "$REPLY" )
done < <(your-subshell-here)
...but this is perfectly side-effect free:
declare -a array
while IFS= read -r; do
array+=( "$REPLY" )
done < <(your-subshell-here)
With bash 4.0 or newer, there's also the option of readarray or mapfile (synonyms for the same operation):
mapfile -t array < <(your-subshell-here)
In examples later added to your answer, you have code along the lines of:
lines=($(egrep "^-o" speccmds.cmd))
The better way to write this is:
mapfile -t lines < <(egrep "^-o" speccmds.cmd)
Are you trying to store the lines of the output in an array, or the words of each line?
lines
mapfile -t arrayname < <(your subshell)
This does not use IFS at all.
words
(your subshell) | while IFS=: read -ra words; do ...
The form var=value command args... puts the var variable into the environment of the command, and does not affect the current shell's environment.
This question already has answers here:
Creating an array from a text file in Bash
(7 answers)
Closed 7 years ago.
I am trying to read a file containing lines into a Bash array.
I have tried the following so far:
Attempt1
a=( $( cat /path/to/filename ) )
Attempt2
index=0
while read line ; do
MYARRAY[$index]="$line"
index=$(($index+1))
done < /path/to/filename
Both attempts only return a one element array containing the first line of the file. What am I doing wrong?
I am running bash 4.1.5
The readarray command (also spelled mapfile) was introduced in bash 4.0.
readarray -t a < /path/to/filename
Latest revision based on comment from BinaryZebra's comment
and tested here. The addition of command eval allows for the expression to be kept in the present execution environment while the expressions before are only held for the duration of the eval.
Use $IFS that has no spaces\tabs, just newlines/CR
$ IFS=$'\r\n' GLOBIGNORE='*' command eval 'XYZ=($(cat /etc/passwd))'
$ echo "${XYZ[5]}"
sync:x:5:0:sync:/sbin:/bin/sync
Also note that you may be setting the array just fine but reading it wrong - be sure to use both double-quotes "" and braces {} as in the example above
Edit:
Please note the many warnings about my answer in comments about possible glob expansion, specifically gniourf-gniourf's comments about my prior attempts to work around
With all those warnings in mind I'm still leaving this answer here (yes, bash 4 has been out for many years but I recall that some macs only 2/3 years old have pre-4 as default shell)
Other notes:
Can also follow drizzt's suggestion below and replace a forked subshell+cat with
$(</etc/passwd)
The other option I sometimes use is just set IFS into XIFS, then restore after. See also Sorpigal's answer which does not need to bother with this
The simplest way to read each line of a file into a bash array is this:
IFS=$'\n' read -d '' -r -a lines < /etc/passwd
Now just index in to the array lines to retrieve each line, e.g.
printf "line 1: %s\n" "${lines[0]}"
printf "line 5: %s\n" "${lines[4]}"
# all lines
echo "${lines[#]}"
One alternate way if file contains strings without spaces with 1string each line:
fileItemString=$(cat filename |tr "\n" " ")
fileItemArray=($fileItemString)
Check:
Print whole Array:
${fileItemArray[*]}
Length=${#fileItemArray[#]}
Your first attempt was close. Here is the simplistic approach using your idea.
file="somefileondisk"
lines=`cat $file`
for line in $lines; do
echo "$line"
done
#!/bin/bash
IFS=$'\n' read -d '' -r -a inlines < testinput
IFS=$'\n' read -d '' -r -a outlines < testoutput
counter=0
cat testinput | while read line;
do
echo "$((${inlines[$counter]}-${outlines[$counter]}))"
counter=$(($counter+1))
done
# OR Do like this
counter=0
readarray a < testinput
readarray b < testoutput
cat testinput | while read myline;
do
echo value is: $((${a[$counter]}-${b[$counter]}))
counter=$(($counter+1))
done