1

I have a bash script that requires a glob expression as a parameter. However I am having trouble using inputs as globs i.e say my input is

Shell_script '*.c'

and my code is iterating through an array of files and filtering them through pattern matching. In this case files which do not have the .c extension. (In this example, the first input could be any pattern otherwise)

    count=${#array[@]}
    for (( q = 0; q < count; q++ ));
    do
         if [[ ${array[q]}  == $1 ]]; then
              :
         else unset array[q]
         fi
    done
    .....

Any ideas?

6
  • *.c isn't a valid regex. Are you trying to filter a list of files ? it appears that what you are trying to use is some kind of glob pattern. Commented Feb 25, 2016 at 17:51
  • Yes Im trying to filter a list of files Commented Feb 25, 2016 at 17:54
  • Can't you just loop over the files using something like: for file in *.c do #$file is your file done Commented Feb 25, 2016 at 17:59
  • I'm trying to do it with *.c as an argument to the shell script Commented Feb 25, 2016 at 18:01
  • Why pass the glob as an argument, rather than pass the matched files as a list of arguments? That's the usual approach, and how, for instance ls *.c works. Commented Feb 25, 2016 at 18:07

2 Answers 2

1

Matching array contents against a glob is entirely possible:

#!/bin/bash

# this array has noncontiguous indexes to demonstrate a potential bug in the original code
array=( [0]="hello.c" [3]="cruel.txt" [5]="world.c" )

glob=$1
for idx in "${!array[@]}"; do
  val=${array[$idx]}
  if [[ $val = $glob ]]; then
    echo "File $val matches glob expression $glob" >&2
  else
    echo "File $val does not match glob expression $glob; removing" >&2
    unset array[$idx]
  fi
done

Similarly, you can expand a glob against filesystem contents, though you'll want to clear IFS first to avoid string-splitting:

# here, the expectation is that your script would be invoked as: ./yourscript '*.c'

IFS=
for f in $1; do
  [[ -e $f || -L $f ]] || { echo "No file matching $f found" >&2; }
  echo "Iterating over file $f"
done

That said, in general, this is extremely unidiomatic, as opposed to letting the calling shell expand the glob before your script is started, and reading the list of matched files off your argument vector. Thus:

# written this way, your script can just be called ./yourscript *.c
for f; do
  [[ -e $f || -L $f ]] || { echo "No file matching $f found" >&2; }
  echo "Iterating over file $f"
done
Sign up to request clarification or add additional context in comments.

14 Comments

why is it [[ $val = $glob]] and not [[ $val == $glob]]
@DarthVeder, because == isn't a valid operator in POSIX test. See the standards document at pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html, and it clearly documents the only valid string comparison operator as =; bash adds ==, but that's an extension, and it's better habit to be accustomed to the standards-compliant, and thus widely compatible, approach.
If i switch the arguments in the if statement to: if [[ $val == *.c ]] the glob statement works with the example you shared above. I was returned an empty list
You're making two changes at once. Change the == to only = while leaving the $glob vs *.c unmodified, and you'll see that the former change is not the one with any effect.
...the most likely explanation is that $glob is incorrectly assigned. Use bash -x yourscript '*.c' to trace its execution, and watch the assignment.
|
0

You can loop over your list of files like this. If you run your script as ./test.sh "*.c". Then inside your script you can do:

for file in $1
do
  #use your file
done

1 Comment

This is possible, but it's also a bad idea without some care (such as clearing IFS). Look what happens when the pattern contains a space.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.