Sign up ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. It's 100% free, no registration required.

I couldn't think of a better way of wording the question title. I know there has to be an easy answer or simple alternative to doing what I'm trying to do, but I just cannot come up with anything at the moment.

Below is some sort of pseudo-code for what I'm trying to accomplish:

#!/bin/bash

# I already have the variable below figured out (positive integer):
numlines=$([returns number of lines containing specific characters in a file])

# This is basically what I want to do with it:
for i in {1..$numlines}; do
    # the part below is already figured out as well:        
    do some other stuff
done

I can execute it fine from the command line by inserting the actual number in the `{1..n}' sequence. I just need to know if it's possible to include a variable here and how to go about doing it.

  • I have tried exporting it
  • I have tried putting the variable itself in curly braces inside the sequence: {1..${numlines}}
  • I have tried putting it in double-quotes hoping it would expand: {1.."$numlines"}
  • I have tried escaping the $: {1..\$numlines}

Do I need to use a set -[something] command in order for this variable to get expanded? I have even tried some forms of using eval...all to no avail.

I just need to know if there is something simple or obscure that I am missing or if this is even possible before I waste anymore time on it.

I could throw together a really, really hackish way of doing it to make it work as needed, but I'd like to avoid that if at all possible and learn the right way to go about doing it.

share|improve this question

3 Answers 3

up vote 8 down vote accepted

Unfortunately, there is no way to use a variable in that expansion (AFAIK), since variable expansion happens after brace expansion.

Fortunately, there's a tool that does the same job.

for i in $(seq 1 $numlines); do
    # stuff
done

seq is from GNU coreutils; no idea how to do it in POSIX.

share|improve this answer
1  
(Plus 1). seq is good for GNU systems and, if I recall correctly, the most recent OSX. On other BSD systems, one can use jot instead. – John1024 23 hours ago
    
seq works perfectly. Thank you so much for your prompt answer. – rubynorails 23 hours ago
    
This was not part of my question -- but what is the syntax (if any) for doing this in reverse order, such as {16..1}? $(seq $numlines 1) did not work. I guess I can always man seq, but just wondering if anyone knew off the top of their head. – rubynorails 23 hours ago
    
Just figured out how to do it in reverse from this link -- for i in $(seq $numlines -1 1) – rubynorails 23 hours ago
    
seq ${numlines} -1 0 – DopeGhoti 23 hours ago

If you must avoid seq, which as Tom Hunt points out seems to be the usual solution to this, then an eval definitely can do it (though, I wouldn't encourage it):

eval 'for i in {1..'$numlines'}; do echo $i; done'

You can stay POSIX by avoiding the {} expansion, and simply do math and integer comparisons on $numlines:

while [ ! "$numlines" -eq 0 ]; do
     echo "$numlines"
     : $((numlines-=1))
done

Outside of POSIX, bash and ksh and zsh also have C-style for loops:

for((i=0; i<numlines; i++)); do echo $i; done
share|improve this answer
1  
I really appreciate this answer as well. While seq worked fine for my scenario and seemed to be the simplest solution, it is good to know that there are other (even POSIX) alternatives. Thanks for this. – rubynorails 23 hours ago
1  
There's really no reason to use eval; if you have brace expansion, you have the C-style loop. – chepner 20 hours ago
    
@PSkocik - If I could choose 2 answers, I would also choose this one. When I came across the fact that I needed to do this in reverse, your eval example was the simplest and would have saved me from having to search for an alternate way of doing it using seq. The while loop is a bit bulky for me. I like to keep things short and sweet, and I never could get the C-style for loop to work by giving i the value of 0 or 1. It never returned correctly and was always a little off. I'm sure it could be tweaked to work correctly, but these are definitely helpful solutions, nonetheless. – rubynorails 20 hours ago
    
The eval approach is problematic if there is anything non-trivial inside the body of the loop. I imagine it would not be very readable if you needed to nest two such loops. – kasperd 12 hours ago

Sure. If you want a for loop that increments an integer variable, use the form of the for loop that increments an integer variable (or more generally performs arithmetic on the loop variable(s)).

for ((i=1; i<=numlines; i++)); do … done

This construct works in bash (and ksh93 and zsh), but not in plain sh. In plain sh, use a while loop and the test ([ … ]) construct.

i=1
while [ "$i" -le "$numlines" ]; do
  …
  i=$((i+1))
done
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.