Project

General

Profile

1 3772 aaronmk
#!/bin/bash
2 3771 aaronmk
# Expands bash-style {} expressions.
3
# See <http://www.gnu.org/software/bash/manual/bash.html#Brace-Expansion>.
4 3774 aaronmk
# Usage: [env expand_braces_debug=1] self <in >out
5 3771 aaronmk
# Filters each line from stdin to stdout.
6
# Warning: Due to shell limitations, a blank line will be interpreted as EOF.
7
8
sedEreFlag="$(test "$(uname)" = Darwin && echo E || echo r)"
9
10
sed () { "$(which sed)" -"$sedEreFlag" "$@";}
11
12
echoEach () { local line; for line in "$@"; do echo "$line"; done; }
13
14 3774 aaronmk
replaceRecursive ()
15
{
16
    local sedExpr="$1" str="$2" prevStr
17
    while true; do
18
        prevStr="$str"
19
        str="$(echo "$str"|sed -e "$sedExpr")"
20
        test "$str" = "$prevStr" && break # done if nothing replaced
21
    done
22
    echo "$str"
23
}
24
25
debug ()
26
{
27
    local label="$1" str="$2"
28
    test -n "$expand_braces_debug" && (echo "** $label:"; echo "$str") >&2
29
}
30
31 3772 aaronmk
singleQuote="'"
32
singleQuoteEsc="'\''"
33
CR=$'\r'
34
LF=$'\n'
35 3774 aaronmk
nonBracket="[^][]*" # matches non-[] chars
36
brackets="($nonBracket\[$nonBracket\])*$nonBracket"
37 3771 aaronmk
38
while true; do
39 3778 aaronmk
    read -r # get next line from stdin; strips trailing newlines; -r: raw input
40 3772 aaronmk
    line="$REPLY"
41 3771 aaronmk
    test -z "$line" && break # EOF if no line or empty line
42 3772 aaronmk
43 3774 aaronmk
    line="${line//$CR/}" # remove \r
44
    origLine="$line"
45
    debug orig "$line"
46
47
    line="${line//$singleQuote/$singleQuoteEsc}" # escape single quotes
48
    line="$(echo "$line"|sed -e "s/[^{,}]+/'&'/g")" # escape non-brace chars
49
    # Escape commas inside [], which should not be used for {}.
50
    # Note that '' around "," are closing and opening the escaped string,
51
    # not the other way around.
52
    line="$(replaceRecursive "s/(\[$nonBracket)','/\1,/g" "$line")" # innermost
53
    # Also support one level of nesting, which is the most we currently use.
54
    line="$(replaceRecursive "s/(\[$brackets)','/\1,/g" "$line")" # 1st outer
55
    debug escaped "$line"
56
57
    debug expanded
58
    eval echoEach $line # brace-expand $line
59 3771 aaronmk
done