2

Here is a small[but complete] part of my bash script that finds and outputs all files in mydir if the have the prefix from a stored array. Strange thing I notice is that this script works perfectly if I take out the "-maxdepth 1 -name" from the script else it only gives me the files with the prefix of the first element in the array.

It would be of great help if someone explained this to me. Sorry in advance if there is some thing obviously silly that I'm doing. I'm relatively new to scripting.

#!/bin/sh
DIS_ARRAY=(A B C D)
echo "Array is : "
echo ${DIS_ARRAY[*]}
for dis in $DIS_ARRAY
do
    IN_FILES=`find /mydir  -maxdepth 1 -name "$dis*.xml"`
    for file in $IN_FILES
    do
    echo $file
    done
done

Output:

/mydir/Abc.xml
/mydir/Ab.xml
/mydir/Ac.xml

Expected Output:

/mydir/Abc.xml
/mydir/Ab.xml
/mydir/Ac.xml
/mydir/Bc.xml
/mydir/Cb.xml
/mydir/Dc.xml
jww
  • 97,681
  • 90
  • 411
  • 885
Karen
  • 37
  • 1
  • 6

2 Answers2

3

The loop is broken either way. The reason why

IN_FILES=`find mydir  -maxdepth 1 -name "$dis*.xml"`

works, whereas

IN_FILES=`find mydir "$dis*.xml"`

doesn't is because in the first one, you have specified -name. In the second one, find is listing all the files in mydir. If you change the second one to

IN_FILES=`find mydir -name "$dis*.xml"`

you will see that the loop isn't working.

As mentioned in the comments, the syntax that you are currently using $DIS_ARRAY will only give you the first element of the array.

Try changing your loop to this:

for dis in "${DIS_ARRAY[@]}"

The double quotes around the expansion aren't strictly necessary in your specific case, but required if the elements in your array contained spaces, as demonstrated in the following test:

#!/bin/bash

arr=("a a" "b b")

echo using '$arr'
for i in $arr; do echo $i; done
echo using '${arr[@]}'
for i in ${arr[@]}; do echo $i; done
echo using '"${arr[@]}"'
for i in "${arr[@]}"; do echo $i; done

output:

using $arr
a
a
using ${arr[@]}
a
a
b
b
using "${arr[@]}"
a a
b b

See this related question for further details.

Tom Fenech
  • 72,334
  • 12
  • 107
  • 141
  • That worked, thanks! Could you be kind enough to explain why the previous method didn't work vs this method? Or is it just a standard syntax? – Karen Mar 12 '14 at 23:00
  • 1
    @Karen: Referencing an array variable without the subscript `[@]` (or the equivalent `[*]`) returns only the *first* element's value. – mklement0 Mar 12 '14 at 23:04
  • @mklement0, Tom Thanks for the explanation! Though I still don't understand how it worked with this exact syntax before and why the addition of -maxdepth or -name would affect it in this way. – Karen Mar 12 '14 at 23:11
  • @Karen it's hard to say, I suspect that there may be something else that has caused this to happen. Can you post the two bits of code that produce the two behaviours? – Tom Fenech Mar 12 '14 at 23:25
  • Its the exact same code with a change in this line **IN_FILES=`find /mydir "$dis*.xml"`** vs **IN_FILES=`find /mydir -maxdepth 1 -name "$dis*.xml"`**. The previous one loops through the entire array while the second one just uses the first element of the array. – Karen Mar 12 '14 at 23:56
  • @TomFenech Thanks for solving that mystery. It had me stumped. – Karen Mar 13 '14 at 19:20
2

@TomFenech's answer solves your problem, but let me suggest other improvements:

#!/usr/bin/env bash

DIS_ARRAY=(A B C D)
echo "Array is : "
echo ${DIS_ARRAY[*]}
for dis in "${DIS_ARRAY[@]}"
do
    for file in "/mydir/$dis"*.xml
    do
        if [ -f "$file" ]; then
            echo "$file"
        fi
    done
done
  • Your shebang line references sh, but your question is tagged bash - unless you need POSIX compliance, use a bash shebang line to take advantage of all that bash has to offer
  • To match files located directly in a given directory (i.e., if you don't need to traverse an entire subtree), use a glob (filename pattern) and rely on pathname expansion as in my code above - no need for find and command substitution.
  • Note that the wildcard char. * is UNquoted to ensure pathname expansion.
  • Caveat: if no matching files are found, the glob is left untouched (assuming the nullglob shell option is OFF, which it is by default), so the loop is entered once, with an invalid filename (the unexpanded glob) - hence the [ -f "$file" ] conditional to ensure that an actual match was found (as an aside: using bashisms, you could use [[ -f $file ]] instead).
mklement0
  • 382,024
  • 64
  • 607
  • 775