How to copy files whose name contains number from 20 to 32

7,938

Solution 1

| is the pipeline operator.

cp -r ~/copyDest/*2[0-9]|3[0-2]* ~/pasteDest

is the cp command piped to the command whose name is the first file expanded from the 3[0-2]* glob. For | to be a glob alternation operator, it has be within (...) in zsh (but zsh has a dedicated operator for number range matching) and @(...) in ksh (or bash with extglob on).

So, with zsh:

cp -r ~/copyDest/(*[^0-9]|)<20-32>(|[^0-9]*) ~/pasteDest

Without the (*[^0-9]|), it would also match on foo120

With ksh or bash -O extglob (or use shopt -s extglob within bash) or zsh -o kshglob (set -o kshglob within zsh), the equivalent (except for the order in which the files are copied) would look like:

(
  LC_ALL=C
  cp -r ~/copyDest/?(*[^0-9])*(0)@(2[0-9]|3[0-2])?([^0-9]*) ~/pasteDest
)

With ksh or bash, on most systems and most locales other than C, [0-9] matches a lot more characters than 0123456789, hence the LC_ALL=C (which also affects the glob expansion sorting order). If your file names contain only ASCII characters, you may omit it, as I don't think any locale on any sane system would have ASCII characters other than 0123456789 matched by [0-9]. Other alternative is to replace [0-9] with [0123456789].

Also note that except in zsh -o kshglob, if the pattern doesn't match any file, cp will be called with a literal .../?(*[^0-9])*(0)@(2[0-9]|3[0-2])?([^0-9]*) argument (a valid though unlikely file name) which if it exists would then be copied (or cp would return an error otherwise). In bash, you can use the failglob option to get a behaviour closer to zsh's saner one (of cancelling the command if the pattern doesn't match).

Above we take special care of copying files named foo20.txt, foo00020.txt, but not foo120.txt or foo200.txt (even though their name contains 20). It still copies foo32.12.txt or foo-1E-20.txt or foo0x20.txt files.

If you still want to copy foo120 or foo200 files, then it becomes much simpler:

  • zsh:

    cp -r ~/copyDest/*<20-32>* ~/pasteDest
    
  • bash -O extglob and co:

    cp -r ~/copyDest/*@(2[0123456789]|3[012])* ~/pasteDest
    

Solution 2

You failed to read the manual on shell pattern matching and assumed that it is the same as what is commonly called "regular expressions". Just the fact that the * operator, that you use in your example, has a different meaning should be a hint that they are not the same.

With bash (and some other shells) you can use the {,} operator for the desired effect:

cp -r ~/copyDest/*{2[0-9],3[0-2]}* ~/pasteDest

But beware there are differences. This is the same as writing

cp -r ~/copyDest/*2[0-9]* ~/copyDest/*3[0-2]* ~/pasteDest

That means if either of the patterns doesn't match any file, it will be passed as an argument to cp, and cp will complain that the file doesn't exist. You can set the nullglob shell option to avoid that.

Solution 3

bash approach. As a bonus it prints the numbers for which a matching file was not fou nd.

[steve@instance-2 ~]$ find copyDest pasteDest
copyDest
copyDest/file15
copyDest/file20
copyDest/file25
copyDest/file32
copyDest/file33
pasteDest
[steve@instance-2 ~]$ cp -pr ~/copyDest/*{20..32}* pasteDest
cp: cannot stat ‘/home/steve/copyDest/*21*’: No such file or directory
cp: cannot stat ‘/home/steve/copyDest/*22*’: No such file or directory
cp: cannot stat ‘/home/steve/copyDest/*23*’: No such file or directory
cp: cannot stat ‘/home/steve/copyDest/*24*’: No such file or directory
cp: cannot stat ‘/home/steve/copyDest/*26*’: No such file or directory
cp: cannot stat ‘/home/steve/copyDest/*27*’: No such file or directory
cp: cannot stat ‘/home/steve/copyDest/*28*’: No such file or directory
cp: cannot stat ‘/home/steve/copyDest/*29*’: No such file or directory
cp: cannot stat ‘/home/steve/copyDest/*30*’: No such file or directory
cp: cannot stat ‘/home/steve/copyDest/*31*’: No such file or directory
[steve@instance-2 ~]$ find copyDest pasteDest
copyDest
copyDest/file15
copyDest/file20
copyDest/file25
copyDest/file32
copyDest/file33
pasteDest
pasteDest/file20
pasteDest/file25
pasteDest/file32
[steve@instance-2 ~]$

Solution 4

When I need to deal with numbered files, I tend to use a for loop and seq:

for i in $(seq 20 32); do cp -r ~/copyDest/*${i}* ~/pasteDest; done

Solution 5

Yoe need to do pattern matching, NOT regex matching. Look into your shell's man page. Try something like

ls  *2[0-9]* *3[0-2]*

i.e. implement the alternation by supplying two patterns.

Share:
7,938
user317427
Author by

user317427

Updated on September 18, 2022

Comments

  • user317427
    user317427 almost 2 years

    I want to copy files from the copyDest to pastDest that contain number from 20 to 32. What am I dong wrong?

    cp -r ~/copyDest/*2[0-9]|3[0-2]* ~/pasteDest
    

    Thanks.

  • Stéphane Chazelas
    Stéphane Chazelas over 5 years
    Note that it would copy a foo200.txt or foo120.txt file as well.
  • Stéphane Chazelas
    Stéphane Chazelas over 5 years
    Note that it would copy a foo200.txt or foo120.txt file as well.
  • Stéphane Chazelas
    Stéphane Chazelas over 5 years
    Note that it would copy a foo200.txt or foo120.txt file as well.
  • RudiC
    RudiC over 5 years
    @Stéphane Chazelas: which is what the OP requested: "contain number from 20 to 32". 120 does contain such. But, I see your point.
  • Stéphane Chazelas
    Stéphane Chazelas over 5 years
    Yes, it was a note, not an objection. Something the OP possibly hadn't considered.
  • Baptiste Candellier
    Baptiste Candellier over 5 years
    There's no need to be condescending... at first sight they look similar. The rest of the answer is useful though, thanks.
  • mr.spuratic
    mr.spuratic over 5 years
    This approach (or zsh's <20-32>) is cleanest as it doesn't require the human to reverse engineer a pattern from a numeric sequence.
  • studog
    studog over 5 years
    The OP's attempt, if it worked, would also match foo200.txt or foo120.txt so presumably those matches are prohibited by the larger unstated context of OP's situation; perhaps the log files are only numbered from 1 - 64.
  • FeRD
    FeRD over 5 years
    I mean, absent some sort of additional constraints to the contrary, foo200.txt does contain the number "20" in its filename.
  • ybungalobill
    ybungalobill about 3 years
    Also the -w switch is useful if the numbers need to be padded with zeros: seq -w 0 100.