Question

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
Was it helpful?

Solution

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.

OTHER TIPS

@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).
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top