Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
237 views
in Technique[技术] by (71.8m points)

bash - Brace expansion with variable?


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Workarounds for not being able to use variables in a sequence brace expression:

  • If the intent is merely to iterate over numbers in a range - as in the OP's case - the best choice is not to use brace expansion, but instead use bash's C-style loop - see user000001's answer.

    • If the specific numbers aren't important and you simply need to execute a loop body a specified number of times, Cole Tierney's answer is an option.
  • If use of brace expansion is desired nonetheless:

    • If you do NOT need the numbers in the list to have a prefix or postfix, use the seq utility with an unquoted command substitution (small caveat: seq is NOT a POSIX utility, but it is widely available); e.g.

      • echo $(seq 3) -> 1 2 3; start number 1 implied
        • echo $(seq -f '%02.f' 3) -> 01 02 03 - zero-padded
      • echo $(seq 2 4) -> 2 3 4; explicit start and end numbers
      • echo $(seq 1 2 5) -> 1 3 5; custom increment (the 2 in the middle)
    • If you DO need the numbers in the list to have a prefix or postfix, you have several choices:

      • Use the seq utility with its -f option for providing a printf-style format string (as used above for zero-padding), or pure Bash workarounds based on eval (extra care needed!) or building an array in a loop, all of which are detailed in this answer.
      • You could also consider implementing the functionality generically, such as by writing a custom shell function or a custom script with utilities such as awk or perl.

Example of safe use of eval with variables driving a sequence brace expression:

The variables are validated beforehand, to make sure they contain decimal integers.

from=1 to=3  # sample values

# Ensure that $from and $to are decimal numbers and abort, if they are not.
(( 10#$from + 10#$to || 1 )) 2>/dev/null || { echo "Need decimal integers" >&2; exit 1; }

eval echo "A{$from..$to}"  # -> 'A1 A2 A3'

General overview of brace expansion

The main purpose of brace expansion is to expand to a list of tokens with each token having an optional prefix and/or postfix; brace expansions must be unquoted and come in 2 flavors:

  • a fixed series (list) of comma-separated strings - variables supported
    • specifies and expands to a fixed number of tokens (2 or more); e.g.:
    • echo A{b,c,d} -> Ab Ac Ad, i.e., 3 tokens, as implied by the number of args.
    • echo {/,$HOME/}Library e.g., -> /Library /User/jdoe/Library
    • Variable references - and even globs - are supported, but note that they get expanded after brace expansion, in its result, in the course of normal evaluation.
  • a sequence expression (range) with .., typically numerical - variables NOT supported

    • expands to a variable number of tokens, driven by literal start and end points (for historical reasons, use of variables is NOT supported - see the comments on user000001's answer):
      • [rare] strings: only single English letters allowed; e.g. {a..c}
      • numbers: decimal integers only; e.g., {1..10}, {10..1}, {-1..2}
        • example with prefix and postfix: A{1..3}# -> A1# A2# A3#
        • broken example with variables: {$from..$to} # !! FAILS - $from and $to are interpreted as literals and therefore not recognized as either a single letter or a decimal integer - no brace expansion is performed (see below).
          • by contrast, using variables does work in zsh and ksh.
      • bash 4+ adds two features:
        • optional increment step value:
          • echo A{1..5..2} -> A1 A3 A5 - numbers incremented by 2
        • ability to zero-pad:
          • echo A{001..003} -> A001 A002 A003
  • An invalid brace expression is not expanded (treated like a regular unquoted string, with { and } treated as literals):

    • echo {} -> '{}' - invalid as a brace expr.: at least 2 ,-separated tokens needed
      • This allows the use of unquoted {} with find, for instance.
    • echo {1..$to} -> '{1..<value-of-$to>}' - invalid as a brace expr. in bash: variables not supported; however, valid in ksh and zsh.
    • (fish, by contrast, expands any {...} sequence; similarly, zsh has option BRACE_CCL (OFF by default) for expanding individual characters inside {..}, which effectively causes expansion of any nonempty {...} sequence.)

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...