0

I am running the following bash code:

num=$(ls -1 $ini/*.ini | wc -l)
echo "Running $num simulations..."
for i in {1..$num};
do
    echo "a"
done

And I get the following output:

Running 24 simulations...
a

It should print 24 lines of 'a', but it doesn't. What should I change? Thanks!

4 Answers 4

1

The curly brackets don't expand variables. You could use

for i in $(seq $num); do
    echo "a"
done

See e.g. man bash:

[...]

A sequence expression takes the form {x..y[..incr]}, where x and y are either integers or single characters, and incr, an optional increment, is an integer. When integers are supplied, the expression expands to each number between x and y, inclusive.

[...]

Brace expansion is performed before any other expansions, and any characters special to other expansions are preserved in the result. It is strictly textual. Bash does not apply any syntactic interpretation to the context of the expansion or the text between the braces.

[...]

Sign up to request clarification or add additional context in comments.

1 Comment

Note to OS X users: the optional-increment feature is NOT available as of OS X 10.8.2, which comes with bash 3.2.48.
1

Try:

for (( i=0; i < $num; i++ ))
do
    echo "a"
done

Comments

0

The brace expansion only works for literals, it does not expand variables.

Possible workaround:

for i in $(seq 1 $num) ; do

Comments

0

Read:

Disregard answers involving seq(1). cdarke's answer demonstrates correct iteration.

Also note that this is a bash-specific issue. Other shells with brace expansion will evaluate parameter expansions first, but there are tradeoffs.

10 Comments

Please don't JUST tell us WHAT not to do, but also WHY NOT. Why not use seq? Performance? Tradeoffs (convenience vs. speed)?
@mklement Word-splitting command substitution output is a bad idea, and any for x in $(anything) pattern is problematic. Using seq for iteration is pointless because there are built-in ways to do this that are faster, shorter, and portable. seq is a non-standard command. Not understanding this principle is also what led to the much worse mistake of the questioner using $(ls). I would rather not reiterate the same FAQs on basic usage for almost every answer to questions tagged with bash. Maybe it's not as bad elsewhere on S/O.
Thanks for clarifying; good to know that seq is non-standard. Yes, using (( ... )) is faster and the better choice, but it's clearly not shorter. There is nothing wrong with the OP's use of $(ls ... | wc -l) - it always returns only a single, white-space-free token, so there's no danger of undesirable word-splitting (there IS unwanted leading whitespace in the result, but that's a separate issue). Generally, for x in $(...) is potentially problematic, but it definitely has its uses: all that is needed for proper line-by-line processing is to set $IFS to $'\n' (temporarily).
@mklement There are a number of shorter constructs. while ((n++<num)) and for((n=0;++n<num;))do to name a few. As for $(ls), it's always incorrect. There are other issues aside from whitespace, the most important being globbing (and files that contain glob characters). Filenames may contain newlines. ls isn't for use in scripts. Use globs directly. Always quote expansions. There are some limited use cases for word-splitting in POSIX sh where in the absence of arrays then best you can do is wordsplit and prey. In Bash, and ksh-like shells, there are no significant remaining uses.
Using while ((n++<num)) without explicit initialization of n is asking for trouble. for((n=0;++n<num;))do hampers readability. Agreed: for enumeration of files matching a name pattern, globbing is absolutely the way to go, for all the reasons you state. By contrast, the OP used ls -1 in a command substitution to count matching files: works fine, even with filenames with embedded newlines. I'm genuinely curious: how would you count matching files (other than using a counter in a globbing loop)? I eventually followed the links in your answer - instructive, in-depth stuff; thanks.
|

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.