bash Reading array from file - arrays

I've already read a lot of questions concerning reading and writing in ARRAY in bash. I could not find the solution to my issue.
Actually, I've got a file that contains the path of a lot of files.
cat MyFile
> ~/toto/file1.txt
> ~/toto/file2.txt
> ~/toto/file3.txt
> ~/toto/file4.txt
> ~/toto/file5.txt
I fill an array ARRAY to contain this list:
readarray ARRAY < MyFile.txt
or
while IFS= read -r line
do
printf 'TOTO %s\n' "$line"
ARRAY+=("${line}")
done <MyFile.txt
or
for line in $(cat ${MyFile.txt}) ;
do echo "==> $line";
ARRAY+=($line) ;
done
All those methods work well to fill the ARRAY,
echo "0: ${ARRAY[1]}"
echo "1: ${ARRAY[2]}"
> 0: ~/toto/file1.txt
> 1: ~/toto/file2.txt
This is awesome.
but my problem is that if I try to diff the content of the file it does not work, it looks like the it does not expand the content of the file
diff ${ARRAY[1]} ${ARRAY[2]}
diff: ~/toto/file1.txt: No such file or directory
diff: ~/toto/file2.txt: No such file or directory
but when a print the content:
echo diff ${ARRAY[1]} ${ARRAY[2]}
diff ~/toto/file1.txt ~/toto/file2.txt
and execute it I get the expected diff in the file
diff ~/toto/file1.txt ~/toto/file2.txt
3c3
< Param = {'AAA', 'BBB'}
---
> Param = {'AAA', 'CCC'}
whereas if I fill ARRAY manually this way:
ARRAY=(~/toto/file1.txt ~/toto/file2.txt)
diff works well.
Does anyone have an idea?
Thanks a lot
Regards,
Thomas

Tilde expansion does not happen when you use variable substitution from ${ARRAY[index]}.
Put the full path to the files in MyFile.txt and run your code again.

Related

Multidimensional array in bash via associative array

I'm trying to load files from the directory to the associative array with the access like "FDN,4" where FND is the basename of the file and 4 - is the line number:
loadFiles() {
local iter
local comname
local lines
echo "# Loading files"
find ./sys -type f | while read iter
do
comname=$(basename "$iter" .aic)
echo "# $comname"
local i
i=0
while IFS= read -r line
do
commands["$comname,$i"]="$line"
#echo "$comname,$i = ${commands[$comname,$i]}"
((i++))
done < "$iter"
[[ -n $line ]] && commands["$comname,$i"]="$line"
done
}
loadFiles
echo "POP,4 = ${commands[POP,4]}"
I'm getting nothing, the ./sys/dir/POP.aic file exists and the 4th line in this file too. Commented echo inside the cycle shows that value assigns.
Can anyone, please, help and show me where I'm wrong?
Found the root of evil - the subshell. echo "1 2 3" | while <...> will submit the nex subshell, so the variables will be set only locally. The soultion is to use while <...> done < <(find ./sys -type f)

Iteration with ls and space in name of file

I use bash on Ubuntu and I have some files in a folder, some with space in their name, other non.
I would like an array with file's name.
Example : [foo.txt, I am a file.txt, bar.jpg, etc.]
My code :
for x in "$(ls -1 test/)"; do
fileList+=($x)
done
I get : [foo.txt, I, am, a, file.txt, bar.jpg, etc.]
If I put fileList+=("$x") I get one line array [foo.txt I am a file.txt bar.jpg etc.].
How can I do to get what I want?
Thank you.
Why not use shell globs? E.g.
for x in test/*; do
...
or
filelist=( test/* )
EDIT:
shopt -s nullglob
shopt -s dotglob
might be also wanted.
Try using read, like this:
ls | while read f ; do
echo "$f"
done

Store the output of find command in an array [duplicate]

This question already has answers here:
How can I store the "find" command results as an array in Bash
(8 answers)
Closed 4 years ago.
How do I put the result of find $1 into an array?
In for loop:
for /f "delims=/" %%G in ('find $1') do %%G | cut -d\/ -f6-
I want to cry.
In bash:
file_list=()
while IFS= read -d $'\0' -r file ; do
file_list=("${file_list[#]}" "$file")
done < <(find "$1" -print0)
echo "${file_list[#]}"
file_list is now an array containing the results of find "$1
What's special about "field 6"? It's not clear what you were attempting to do with your cut command.
Do you want to cut each file after the 6th directory?
for file in "${file_list[#]}" ; do
echo "$file" | cut -d/ -f6-
done
But why "field 6"? Can I presume that you actually want to return just the last element of the path?
for file in "${file_list[#]}" ; do
echo "${file##*/}"
done
Or even
echo "${file_list[#]##*/}"
Which will give you the last path element for each path in the array. You could even do something with the result
for file in "${file_list[#]##*/}" ; do
echo "$file"
done
Explanation of the bash program elements:
(One should probably use the builtin readarray instead)
find "$1" -print0
Find stuff and 'print the full file name on the standard output, followed by a null character'. This is important as we will split that output by the null character later.
<(find "$1" -print0)
"Process Substitution" : The output of the find subprocess is read in via a FIFO (i.e. the output of the find subprocess behaves like a file here)
while ...
done < <(find "$1" -print0)
The output of the find subprocess is read by the while command via <
IFS= read -d $'\0' -r file
This is the while condition:
read
Read one line of input (from the find command). Returnvalue of read is 0 unless EOF is encountered, at which point while exits.
-d $'\0'
...taking as delimiter the null character (see QUOTING in bash manpage). Which is done because we used the null character using -print0 earlier.
-r
backslash is not considered an escape character as it may be part of the filename
file
Result (first word actually, which is unique here) is put into variable file
IFS=
The command is run with IFS, the special variable which contains the characters on which read splits input into words unset. Because we don't want to split.
And inside the loop:
file_list=("${file_list[#]}" "$file")
Inside the loop, the file_list array is just grown by $file, suitably quoted.
arrayname=( $(find $1) )
I don't understand your loop question? If you look how to work with that array then in bash you can loop through all array elements like this:
for element in $(seq 0 $((${#arrayname[#]} - 1)))
do
echo "${arrayname[$element]}"
done
This is probably not 100% foolproof, but it will probably work 99% of the time (I used the GNU utilities; the BSD utilities won't work without modifications; also, this was done using an ext4 filesystem):
declare -a BASH_ARRAY_VARIABLE=$(find <path> <other options> -print0 | sed -e 's/\x0$//' | awk -F'\0' 'BEGIN { printf "("; } { for (i = 1; i <= NF; i++) { printf "%c"gensub(/"/, "\\\\\"", "g", $i)"%c ", 34, 34; } } END { printf ")"; }')
Then you would iterate over it like so:
for FIND_PATH in "${BASH_ARRAY_VARIABLE[#]}"; do echo "$FIND_PATH"; done
Make sure to enclose $FIND_PATH inside double-quotes when working with the path.
Here's a simpler pipeless version, based on the version of user2618594
declare -a names=$(echo "("; find <path> <other options> -printf '"%p" '; echo ")")
for nm in "${names[#]}"
do
echo "$nm"
done
To loop through a find, you can simply use find:
for file in "`find "$1"`"; do
echo "$file" | cut -d/ -f6-
done
It was what I got from your question.

how do I output the contents of a while read line loop to multiple arrays in bash?

I read the files of a directory and put each file name into an array (SEARCH)
Then I use a loop to go through each file name in the array (SEARCH) and open them up with a while read line loop and read each line into another array (filecount). My problem is its one huge array with 39 lines (each file has 13 lines) and I need it to be 3 seperate arrays, where
filecount1[line1] is the first line from the 1st file and so on. here is my code so far...
typeset -A files
for file in ${SEARCH[#]}; do
while read line; do
files["$file"]+="$line"
done < "$file"
done
So, Thanks Ivan for this example! However I'm not sure I follow how this puts it into a seperate array because with this example wouldnt all the arrays still be named "files"?
If you're just trying to store the file contents into an array:
declare -A contents
for file in "${!SEARCH[#]}"; do
contents["$file"]=$(< $file)
done
If you want to store the individual lines in a array, you can create a pseudo-multi-dimensional array:
declare -A contents
for file in "${!SEARCH[#]}"; do
NR=1
while read -r line; do
contents["$file,$NR"]=$line
(( NR++ ))
done < "$file"
done
for key in "${!contents[#]}"; do
printf "%s\t%s\n" "$key" "${contents["$key"]}"
done
line 6 is
$filecount[$linenum]}="$line"
Seems it is missing a {, right after the $.
Should be:
${filecount[$linenum]}="$line"
If the above is true, then it is trying to run the output as a command.
Line 6 is (after "fixing" it above):
${filecount[$linenum]}="$line"
However ${filecount[$linenum]} is a value and you can't have an assignment on a value.
Should be:
filecount[$linenum]="$line"
Now I'm confused, as in whether the { is actually missing, or } is the actual typo :S :P
btw, bash supports this syntax too
filecount=$((filecount++)) # no need for $ inside ((..)) and use of increment operator ++
This should work:
typeset -A files
for file in ${SEARCH[#]}; do # foreach file
while read line; do # read each line
files["$file"]+="$line" # and place it in a new array
done < "$file" # reading each line from the current file
done
a small test shows it works
# set up
mkdir -p /tmp/test && cd $_
echo "abc" > a
echo "foo" > b
echo "bar" > c
# read files into arrays
typeset -A files
for file in *; do
while read line; do
files["$file"]+="$line"
done < "$file"
done
# print arrays
for file in *; do
echo ${files["$file"]}
done
# same as:
echo ${files[a]} # prints: abc
echo ${files[b]} # prints: foo
echo ${files[c]} # prints: bar

Unix Shell Script - Putting $input into file

I need to put $input into the file spelled.. but this code leaves the file empty. I am only correcting the first word that comes up in the array, so i'm thinking that when I just hit 'Enter' that it is rewriting the file over? any help?
# store words in file
cat $1 | ispell -l > file
# move words in file into array
array=($(< file))
# remove temp file
rm file
# print out words & ask for corrections
for ((i=0; i<${#array[#]}; i++ ))
do
read -p "' ${array[i]} ' is mispelled. Press "Enter" to keep
this spelling, or type a correction here: " input
echo $input > spelled
done
# show corrections
cat spelled
Use >> instead of > if you want to append when the file exists.
echo $input >> spelled

Resources