0

I have a bash script which seems to lose the value of the readonly constant after the first time thru the for-in loop. For example:

#!/bin/bash
readonly DIR="./groups/"
for output in "${array[@]}"
do
   catstring+="$DIR$output "
done
printf "$catstring"
cat $catstring > outputfile

The array has a bunch of names in it like: file1 file2 file3, etc.

The output from the printf statement is "./groups/file1 file2 file3". What I'm expecting is "./groups/file1 ./groups/file2 ./groups/file3".

Why is bash losing the value of $DIR after the first time thru the for-in loop?

Pretzel
  • 203
  • 3
    I'm guessing your loop is only looping once. "${array[@]}" is evaluating to "file1 file2 file3" not file1 file2 file3 so there is a single (quoted) item to loop over. – DerfK Jul 24 '14 at 18:42
  • Is this the actual script you are using, or is this just an example? My guess is that your actual script is more complicated and you are having the classic subshell issue. A subshell is sometimes used when you don't expect it, and variables set in the subshell aren't returned to the parent shell. – Zoredache Jul 24 '14 at 18:49
  • @Zoredache: yeah, I've stripped out the rest of the script to make it more simple. – Pretzel Jul 24 '14 at 19:39
  • 1
    @DerfK: I think you're right about it only looping over once. That must be what's happening. – Pretzel Jul 24 '14 at 19:43
  • @Pretzel - That would be weird though as the double quoted array expression just means that whitespace is respected in array elements. Try declare -A x ; x[0]=a ; x[1]=b ; x[2]="c d" ; for y in "${x[@]}"; do echo $y; done What happens? – David Tonhofer Jul 24 '14 at 19:49
  • @DerfK: That was it. I removed the double quotes from the ${array[@]} and it appears to work now. If you submit that as an answer, I'll mark it correct. – Pretzel Jul 24 '14 at 19:50
  • @DavidTonhofer: I'll try it tomorrow if I get around to it. Right now I'm just finding bash such a royal PITA that I'd rather just get this script done and move on. Maybe I'll port this to Perl or Python... – Pretzel Jul 24 '14 at 19:52
  • @Pretzel You are actually using sh, not bash.... The syntax is different. PITA ... I know. Good for 20 lines though, use Perl/Python for anything larger. Keep going! – David Tonhofer Jul 24 '14 at 19:54
  • what's the difference? – Pretzel Jul 24 '14 at 19:56
  • I just found the -x switch... This is helpful. – Pretzel Jul 24 '14 at 19:57
  • @DavidTonhofer: if I do "sh -x", it doesn't compile/execute. If I use "bash -x" then it runs. – Pretzel Jul 24 '14 at 19:58
  • @Pretzel Confusingly weird. What OS is this I I may ask? The "+=" is not supported by "sh" either. So you ARE using bash. – David Tonhofer Jul 24 '14 at 20:02
  • @DavidTonhofer: I'm using Clonezilla Live DRBL which is based off of Debian. – Pretzel Jul 24 '14 at 20:08
  • No it's not the bash/sh difference. But over here, the quoted array works as expected, keeping the whitespace "inside" array elements. Oh well... – David Tonhofer Jul 24 '14 at 20:09
  • It's gnu bash 4.2.45 – Pretzel Jul 24 '14 at 20:10
  • 2
    If double-quotes around the array reference are causing trouble, I'm pretty sure there's something wrong with how the array is being constructed; removing the double-quotes should not be necessary. I'd concentrate on figuring out what's building the array wrong. – Gordon Davisson Jul 24 '14 at 20:16
  • 1
    I have 4.2.47. But it's really not the quotes. Quoteless unset x; declare -A x ; x[0]=a ; x[1]=b ; x[2]="c d" ; for y in ${x[@]}; do echo $y; done erroneously yields 4 lines; Quoted unset x; declare -A x ; x[0]=a ; x[1]=b ; x[2]="c d" ; for y in "${x[@]}"; do echo $y; done correctly yields three lines. HOWEVER!! If you managed to put all the elements into the FIRST element of the array, one would EXACTLY get your problem. Ah-hah! – David Tonhofer Jul 24 '14 at 20:18
  • @DavidTonhofer: you're right. I was using whiptail --checklist and it was generating output like: "one" "two" "three" "four" -- so I used: array=$(whiptail --checklist blah blah blah) and it was inserting the entire output as the first element of the array. Do you know how to capture the output of something (like whiptail) and put the quoted results into separate elements of an array? My Google-fu is weak and I'm not coming up with anything... – Pretzel Jul 25 '14 at 12:33
  • @Pretzel The easiest way uses a temporary file; one should check that file creation via mktmp succeeded though by checking the $? return value: unset x; declare -A x; i=0; FILE=mktmp ; if [[ $? != 0 ]]; then exit 1; fi; cat /etc/hosts > $FILE ; while read; do x[$i]="$REPLY"; echo "Read $REPLY; i is now $i"; let i=$i+1; done < $FILE; echo "i is $i"; for y in "${x[@]}"; do echo "<$y>"; done – David Tonhofer Jul 25 '14 at 13:27

1 Answers1

0

You've most likely captured the file list with something like this:

array=$(ls file* )
#or
array="$(ls file*)"

# array looks like:
# array[0]="file1 file2 file3"

You can capture as multiple indices in an array like this instead, using an extra '(' and ')'

array=( $(ls file*) )

# array looks like:
# array[0]="file1"
# array[1]="file2"
# array[2]="file3" 

and then your code will work

or perhaps you used 'read' to read the values:

ls > files.txt
read array < files.txt

then you want to use 'read -a' instead

read -a < files.txt
Chunko
  • 311
  • Better to use array=( file* ) to avoid word splitting. See also http://unix.stackexchange.com/questions/128985/why-not-parse-ls – Kusalananda Feb 12 '17 at 11:57