0

I'm trying to run some command with looping through all files in a directory. The code is:

#!/bin/bash
shopt -s nullglob
INPUT_DIR=$1
OUTPUT_DIR=$2

: ${INPUT_DIR:="."}
: ${OUTPUT_DIR:="."}

files="$INPUT_DIR/*.ttf"

for file in $files
do
    base_file=${file##*/}
    output="$OUTPUT_DIR/${base_file%.*}.woff"
    ttf2woff "$file" "$output" || exit 1
done

I'd expect the double qoutes around $INPUT_DIR/*.ttf would do the magic but apparently it's not:

$> ttf2woff_multi "/Users/ozan/Dropbox/Graphic Library/Google Fonts/fonts-master/ofl/raleway"
Can't open input file (/Users/ozan/Dropbox/Graphic)

and when I print out $FILES I get: /Users/ozan/Dropbox/Graphic Library/Google

What am I missing here?


Edit: files="$INPUT_DIR"/*.ttf instead of files="$INPUT_DIR/*.ttf" doesn't work either...

1
  • 1
    Quotes prevent globbing. Commented Aug 20, 2015 at 8:38

2 Answers 2

1

In addition to the array solution, (which is a good solution), you can also make use of read with process substitution:

INPUT_DIR=${1:=.}
OUTPUT_DIR=${2:=.}

[ -d "$INPUT_DIR" -a -d "$OUTPUT_DIR" ] || {
    printf "error: invalid directory specified (INPUT_DIR or OUTPUT_DIR)\n"
    exit 1
}

while IFS= read -r file; do
    base_file=${file##*/}
    output="$OUTPUT_DIR/${base_file%.*}.woff"
    ttf2woff "$file" "$output" || exit 1
done < <(find "$INPUT_DIR" -type f -iname "*.ttf")
Sign up to request clarification or add additional context in comments.

3 Comments

good one. Probably good to say IFS= in the while to "prevent trimming of leading and trailing whitespace" (mywiki.wooledge.org/BashFAQ/001)
Thanks for more elegant solution. May I ask the reason for using "<" twice for the while loop?
That is what is called process substitution You are executing commands in a subshell (find "$INPUT_DIR" -type f -iname "*.ttf") and then the very specific < <(foo) is the syntax for redirecting the output of that process. Its is similar to command substitution, e.g. say $(sed -e 's/foo/bar/' somefile), but instead of storing the output in a variable, you use the output as you would use stdin.
0

Since you want to loop through a list of files, better store them in an array:

files=("$INPUT_DIR"/*.ttf)

for file in "${files[@]}"
do
    base_file=${file##*/}
    output="$OUTPUT_DIR/${base_file%.*}.woff"
    ttf2woff "$file" "$output" || exit 1
done

Note you were saying "$INPUT_DIR/*.ttf" whereas I am suggesting "$INPUT_DIR"/*.ttf. This is to allow the globbing to behave as intended and expand properly.


The key point here, as Cyrus mentions in comments, is the fact of not quoting, since they prevent globbing.

See an example with some files.

$ ls f*
f1  f2  f3

Store with double quotes... it just matches the string itself:

$ files=("f*")
$ for f in "${files[@]}"; do echo "$f"; done
f*

See how it is expanded if we do not quote:

$ files=(f*)
$ for f in "${files[@]}"; do echo "$f"; done
f1
f2
f3

5 Comments

Makes sense, however when I echo $f I get: "/Users/ozan/Dropbox/Graphic" and "Library/Google" in two separate lines.
@madpoet I don't know what does it mean: is this a directory, a file? Is this what you need?
Sorry it's hard to explain in comment since stackoverflow is stripping the newlines. The input directory ($INPUT_DIR) is "/Users/ozan/Dropbox/Graphic Library/Google Fonts/fonts-master". I'd expect all *.ttf files are echoed under that directory. However it prints out two strings "/Users/ozan/Dropbox/Graphic" and "Library/Google"
@madpoet ah, now I understand. Then you need to quote the directory but not the *: files=("$INPUT_DIR"/*.ttf). I also edited my answer
Ok now I get it. In fact I knew quotes prevent globbing but I was confused when quoting only $INPUT_DIR didn't work either and it prints string in both cases so the answer is to use array...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.