Using variables to copy files with increasing numerical names.

Hello, I have been learning Linux shell commands for about 4 months now. I am trying to write a simple shell script, but I have encountered an issue I do not understand the cause of.

#!/bin/bash

first=1
last=10

cp path/to/files/{$first..$last}.dat ./

When I run my script I get an error.
[user@x dir]$ ./myscript.sh
cp: cannot stat `path/to/file/{1..2}.dat': No such file or directory
[user@x dir]$

However when I run it without variables.

#!/bin/bash

cp path/to/files/{1..10}.dat ./

Running script without variables.
[user@x dir]$ ./myscript.sh

It copies the ten *.dat files from the path/to/files to the directory of myscript.sh. How can I get the script working with variables? Thanks in advance for any help given.

This is a "brace expansion". It is expanded before any other expansions, and it cannot take any variables; read man bash very carefully:

Those strings, integers, or characters are constants, not variables.

2 Likes

Hi
try this workaround

eval cp path/to/files/{$first..$last}.dat ./

... or a loop, that correctly handles special characters in file names, and can verify that the files exist:

for (( i=1; i<=10; i++ )); do
  f=/path/to/files/$i.dat
  [ -f "$f" ] && cp "$f" ./
done

Hi, @MadeInGermany
Then it will meet the condition of the task

for (( i=first; i<=last; i++ )); do
  f=/path/to/files/$i.dat
  [ -f $f -a ! -f $i.dat ] && cp $f ./
done

But closer to me is such a solution

cp path/to/files/[$first-$last].dat ./

Here I got excited. This is correct if the file numbers are in the range 0-9

Hi.

Brace expansion with variables will work correctly with ksh and zsh ... cheers, drl

On a system like:

OS, ker|rel, machine: Linux, 3.16.0-7-amd64, x86_64
Distribution        : Debian 8.11 (jessie) 
ksh 93u+
zsh 5.0.7

Good to know. Bash was first with brace expansion, now ksh and zsh are ahead. Let's see when bash will catch up again.
BTW, Bash-4 has got a number + field alignment expansion:

echo {01..10}
01 02 03 04 05 06 07 08 09 10

that ksh-93 has NOT:

echo {01..10}
1 2 3 4 5 6 7 8 9 10

zsh has got it even with variables:

zsh$ first=1
zsh$ last=10
zsh$ echo {$first..$last}
1 2 3 4 5 6 7 8 9 10
zsh$ first=01
zsh$ echo {$first..$last}
01 02 03 04 05 06 07 08 09 10
1 Like

Using "brace expansion",

(see above), as opposed to "file name globbing". So be careful and test for file existence (as done in other posts / methods above) to avoid running into errors.