Variable scoping issue in Bash [duplicate] - arrays

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 ...)

Related

Bash: Append 'find'-results to indexed array using loop [duplicate]

This question already has answers here:
A variable modified inside a while loop is not remembered
(8 answers)
How can I store the "find" command results as an array in Bash
(8 answers)
Closed last year.
I have the following code which isn't working:
declare -a pids
find /foo/bar/ -iname "*.pid" -print0 | while read -d $'\0' file
do
pids+=("$file")
done
for i in "${pids[#]}"
do
echo $i
done
It should create an indexed array, search the given folder for all PIDs and append them to the array using a while loop however it seems that nothing is happening.
For me it looks like the results of find don't get appended to the array because the for loop at the bottom doesn't echo anything. I'm not sure if the += operant is correct for this but looking at similar question it seems to be. When trying to manually append a value to the array pids, instead of creating a new value on the next index it just adds the string to the first value (index 0).
For example this:
declare -a pids
pids+="foo1"
pids+="foo2"
results in this:
pids[0]="foo1foo2"
instead of:
pids[0]="foo1"
pids[1]="foo2"
Is += not the right operant for this or is something else wrong?

Bash Script | How to read an input through stdin in an array [duplicate]

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).

Command not found assignement array script bash [duplicate]

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"

Elements not being appended to array in loop [duplicate]

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

Saving directory content to an array (bash) [duplicate]

This question already has answers here:
How do you store a list of directories into an array in Bash (and then print them out)?
(4 answers)
Closed 7 years ago.
I need to save content of two directories in an array to compare them later. Thats the solution i write:
DirContent()
{
#past '$1' directorys to 'directorys'
local DIRECTORYS=`ls -l --time-style="long-iso" $1 | egrep '^d' | awk '{print $8}'`
local CONTENT
local i
for DIR in $DIRECTORYS
do
i=+1
CONTENT[i]=${DIR}
done
echo $CONTENT
}
Then when I try to print this array I get empty output. Both directories are not empty. Please tell me what am I doing wrong here.
Thanks, Siery.
The core of this question is answered in the one I marked as a duplicate. Here are a few more pointers:
All uppercase variable names are discouraged as they are more likely to clash with environment variables.
You assign to DIRECTORYS (should probably be "directories") the output of a complicated command, which suffers from a few deficiencies:
Instead of backticks as in var=`command`, the syntax var=$(command) is preferred.
egrep is deprecated and grep -E is preferred.
The grep and awk commands could be combined to awk /^d/ '{ print $8 }'.
There are better ways to get directories, for example find, but the output of find shouldn't be parsed either.
You shouldn't process the output of ls programmatically: filenames can contain spaces, newlines, other special characters...
DIRECTORYS is now just one long string, and you rely on word splitting to iterate over it. Again, spaces in filenames will trip you up.
DIR isn't declared local.
To increase i, you'd use (( ++i )).
CONTENT[i]=${DIR} is actually okay: the i is automatically expanded here and doesn't have to be prepended by a $. Normally you'd want to quote your variables like "$dir", but in this case we happen to know that it won't be split any further as it already is the result of word splitting.
Array indices start at zero and you're skipping zero. You should increase the counter after the assignment.
Instead of using a counter, you can just append to an array with content+=("$dir").
To print the contents of an array, you'd use echo "${CONTENT[#]}".
But really, what you should do instead of all this: a call DirContent some_directory is equivalent to echo some_directory/*/, and if you want that in an array, you'd just use
arr=(some_directory/*/)
instead of the whole function – this even works for weird filenames. And is much, much shorter.
If you have hidden directories (names starts with .), you can use shopt -s dotglob to include them as well.
You can try
for((i=0;i<${#CONTENT[*]};i++))
do
echo ${CONTENT[$i]}
done
instead of echo $CONTENT
Also these change are required
((i=+1))
CONTENT[$i]=${DIR}
in your above code

Resources