list files numbered in a specific range

16

6

I have a set of files in a directory numbered as follows:

file1.txt
file2.txt
...
file30.txt
...

Now, I'd like to run a command on a specific range of files, lets say 18 through 31.

So far I have used the following ways,

three arguments

command file1[8-9].txt file2[0-9].txt file3[0-1].txt

Now suppose I want every other number,

loop

for i in `jot - 18 31 2`; do echo file${i}.txt; done | xargs command

this seems like it's a more reliable loop (spaces work)

for i in `jot - 18 31 2`; do echo "\"file${i}.txt\" "; done | xargs command

but it seems like there should be a simpler way to do this. I am guessing the best solution is to create a bash function that will return the set of files to the command line. Then I could do something like,

command `filerange file 18 31`

Are there better ways or suggestions on doing this efficiently?

I apologize in advance if I missed this same question answered somewhere else on the web or on superuser.

milkypostman

Posted 2011-01-22T15:53:21.537

Reputation: 303

I updated my question to include a solution using a for loop that allows for spaces in filenames using xargs. It seems to work if you don't have bash 4. If you have bash 4, then definitely use brace expansion! See the selected answer. Shameless plug to upgrade Snow Leopard bash

– milkypostman – 2011-01-22T17:08:24.990

Answers

25

Try this:

for file in file{18..31}.txt

It's known as a "sequence expression" and it's part of Bash's brace expansion feature. It works in Bash 3 and 4.

The increment feature is new to Bash 4. You probably have Bash 3.x.

In Bash 4, you can do:

$ for i in {1..6..2}; do echo $i; done
1
3
5

But in Bash 3, you have to do this to get the same results:

$ for ((i=1; i<=6; i+=2)); do echo $i; done

The same form incrementing by one:

$ for ((i=1; i<=6; i++)); do echo $i; done

Any of the numbers can be variables or expressions. However, in a sequence expression the numbers have to be constants

Here is an example using that form on your files:

for ((i=18; i<=31; i++))
do
    echo "file${i}.txt"
done

Another new feature of sequence expressions in Bash 4 is the ability to include leading zeros. This is useful if you want to create (and use) numbered files that can be properly sorted.

In Bash 4:

touch file{001..010}.txt

would create files named "file001.txt" through "file010.txt". Their names will sort in the expected order. Without the leading zeros, "file10.txt" would sort before "file1.txt".

To work with the files, you can use the same leading zero syntax:

for file in file{001..010}.txt; do echo "$file"; done

In Bash 3, if you need leading zeros, you need to pad the value yourself:

for i in {1..10}
do
    printf -v i '%03d' $i 
    echo "file${i}.txt"
done

The printf statement will prepend the value of i with leading zeros so the width is 3, for example ("4" becomes "004").

Edit:

Accommodating spaces in filenames is straightforward:

$ touch "space name "{008..018..3}" more spaces"
$ ls -l sp*
-rw-r--r-- 1 user group 0 2011-01-22 11:48 space name 000008 more spaces
-rw-r--r-- 1 user group 0 2011-01-22 11:48 space name 000011 more spaces
-rw-r--r-- 1 user group 0 2011-01-22 11:48 space name 000014 more spaces
-rw-r--r-- 1 user group 0 2011-01-22 11:48 space name 000017 more spaces
$ for f in "space name "{008..018..3}" more spaces"; do mv "$f" "${f}.txt"; done
$ ls -l sp*
-rw-r--r-- 1 user group 0 2011-01-22 11:48 space name 000008 more spaces.txt
-rw-r--r-- 1 user group 0 2011-01-22 11:48 space name 000011 more spaces.txt
-rw-r--r-- 1 user group 0 2011-01-22 11:48 space name 000014 more spaces.txt
-rw-r--r-- 1 user group 0 2011-01-22 11:48 space name 000017 more spaces.txt

Paused until further notice.

Posted 2011-01-22T15:53:21.537

Reputation: 86 075

Thank You! I don't know where this has been my whole life. The one thing that I can't get to work though is that the increment doesn't seem to work in the OSX version of bash. i.e., I can't get {1..10..2} to work in my OSX bash. – milkypostman – 2011-01-22T16:11:35.113

Well, it's cause Snow Leopard (for some reason) is using bash 3.1. This is a software issue. – milkypostman – 2011-01-22T16:22:13.707

@milkypostman: The increment feature and leading zeros {01..10} are new to Bash 4. You probably have Bash 3.x. If you need the increment feature or to use variables for the range boundaries, then use this form: for ((i=1; i<=10; i+=2} (or i++ for incrementing by one). Any of the numbers can be variables or expressions. Then, to use the value inside the for loop: echo "file${i}.txt". In Bash 3, if you need leading zeros, inside the for loop do:printf -v i '%03d' $iwhich will prepend the value ofi` with leading zeros so the width is 3, for example ("4" becomes "004"). – Paused until further notice. – 2011-01-22T16:26:12.937

Dennis, is there any way you'd update your solution to include the alternate form. Being a comment it's kinda obfuscating what I'm probably supposed to get. – milkypostman – 2011-01-22T16:31:52.020

@milkypostman: See my edited answer. – Paused until further notice. – 2011-01-22T16:50:24.393

I would only add one more comment. The methods you've given above using echo will work as long as you don't have spaces in your filename. At that point they break down. I've had this issue in the past but I avoid files with spaces at all costs. Just a word of warning and why the brace expansion is so nice. Rather than screw with this for loop crap anymore, I went ahead and installed a new version of bash on OSX here is how you do that if others are curious

– milkypostman – 2011-01-22T16:55:45.367

@milkypostman: It's really easy to accommodate spaces in the loops as I've shown them. See my additional edit. The key is to quote the variable. – Paused until further notice. – 2011-01-22T17:52:39.050