0

i'm doing a script to download one file in multiple parts, i'm in the part of joining that files. I can join the parts outside of the script with

cat name.part{0..4} > name.ext

But, if i use this in the script

cat $filename.part{0..$N} > $filename

i get:

cat: Bitcoin-960x623.jpg.part{0..5}: No such file or 
fpilee
  • 1,918
  • 2
  • 22
  • 38

4 Answers4

4

{0..4} is a brace expansion; $N is a variable expansion. Your expression doesn't work as you expect because brace expansion happens before variable expansion:

The order of expansions is: brace expansion, tilde expansion, parameter, variable, and arithmetic expansion and command substitution (done in a left-to-right fashion), word splitting, and filename expansion.

http://www.gnu.org/software/bash/manual/bashref.html#Shell-Expansions

You can use the seq command instead of a brace expansion to generate the required filenames up to arbitrary N:

$ filename=myfile
$ N=4
$ cat $(seq -f "$filename.part%g" 0 $N) > name.ext
$

In the unlikely event seq -f is unavailable, you can use a for loop one-liner instead:

$ cat $(for ((i=0; i<N; i++)); do echo "$filename.part$i"; done) > name.ext
$
Digital Trauma
  • 15,475
  • 3
  • 51
  • 83
  • OTOH seq is nonstandard, nonportable, and may not support `printf`-style format strings. You may have to use `printf "$filename.part%g" $(jot $N 0 $((N+1)) )`, for example. Or just use `extglob`. – kojiro Jan 13 '14 at 21:53
  • This my be true about `seq`, although I found it installed (with `-f`) on all Linux distros I could lay my hands one (including a couple of embedded), as well as OSX. Conversely, the only place I found `jot` installed by default is OSX. Even Ubuntu 12.04 needed it installing. Also, `jot $((N+1)) 0 $N` works better here than `jot $N 0 $((N+1))` – Digital Trauma Jan 13 '14 at 22:24
3

I solved with this.

for (( k=0 ; k<=$N ; k++ ))
do
cat $filename.part$k >> $filename
done
fpilee
  • 1,918
  • 2
  • 22
  • 38
  • 2
    This isn't bad, but if you move the `>> $filename` outside the loop it would be one `open` syscall instead of many `open for append` syscalls. (Also, don't forget to quote $filename.) – kojiro Jan 13 '14 at 21:45
2

You cannot use variables inside curly braces in shell. eval can be used here:

eval "$filename.part{0..$N} > $filename"
anubhava
  • 761,203
  • 64
  • 569
  • 643
2

Assertion: I'm going to argue that this answer is better. (Something I don't do often or lightly.) My rationale is that all of the other answers here at the time of this edit just expand to a range of integers, whether the files matching those integers exist or not. By using glob expansions you guarantee you will not encounter No such file or directory from cat. In all other cases with brace expansion, eval, seq and a for loop you'd have to explicitly check.

If there are fewer than 10 total filenames:

cat "$filename".part[0-9]

will expand as a glob to however many names there are. This is a fast and portable solution, but obviously limited. If there are more than 10 then that pattern will only match the ones with one digit, so you have to get a bit wilier and use extglob:

shopt -s extglob
cat "$filename".part+([0-9])

should do it, but this will only work for the Bourne-Again SHell. (That said, several other modern shells have expansion capabilities similar to extglob.)

kojiro
  • 74,557
  • 19
  • 143
  • 201
  • To play the devil's advocate, the `No such file or directory` errors from `cat` may be very useful. If one of the required input files was missing, this would be silently ignored by the wildcard version, but may have unintended side-effects. – Digital Trauma Jan 13 '14 at 22:41
  • @DigitalTrauma I'm usually on the side of *Errors should never pass silently. Unless explicitly silenced.*, but in this case it would be more effective to check for missing files before executing `cat` at all, lest your unintended side-effects happen *anyway*. – kojiro Jan 14 '14 at 14:21