cp select files recursively?

xSauronx

Lifer
Jul 14, 2000
19,582
4
81
just had this on a test and couldnt seem to figure it out

the goal is to selectively go through a directory "dir1/" and its subdirectories and copy all files starting in a,e,i,o,u and ending in ".py" to dir2/

is there a way to do that with cp? googling around gave a couple of suggestions that didnt seem to work either (one using grep, another using xargs)

any ideas? cant go back and change anything but im curious

thank
 

LCTSI

Member
Aug 17, 2010
93
0
66
use `find` with the -regex option coupled with the -exec option

ie:
find /path/dir1 -type f -regex 'regexpattern' -exec cp {} /path/dir2 \;

the regex pattern is the hard part.
use -iregex if you want it to be case insensitive
 

Gooberlx2

Lifer
May 4, 2001
15,381
6
91
Here're a set of commands that worked for me

I had files <a,e,i,o,u>test.py, mixed in with others, in various nested subdirs

Code:
mkdir /dir2/
cd /dir1/
find . -type d -exec mkdir -p /dir2/{} \;
find . -type f -regex '.+/[aeiou].*\.py' -exec cp {} /dir2/{} \;

edits: had to adjust regex to filter unwanted patterns (ta.py, ablahpy, etc...)
 
Last edited:

xSauronx

Lifer
Jul 14, 2000
19,582
4
81
yeah, thats all way more involved than anything im used to doing (im familiar with linux to a degree, but never got into playing with regex or xargs or anything similar)

thanks for the replies :)
 

Gooberlx2

Lifer
May 4, 2001
15,381
6
91
yeah, thats all way more involved than anything im used to doing (im familiar with linux to a degree, but never got into playing with regex or xargs or anything similar)

thanks for the replies :)

I like this regex tutorial. Regex syntax makes things look cryptic and complicated, but it's really not (and incredibly useful) once you understand how it works.
 

LCTSI

Member
Aug 17, 2010
93
0
66
Code:
mkdir /dir2/
cd /dir1/
find . -type d -exec mkdir -p /dir2/{} \;
>-exec cp {} /dir2/{} \;
edits: had to adjust regex to filter unwanted patterns (ta.py, ablahpy, etc...)

Fails if a directory is named alpha.py, then it returns everything listed under that directory.
 

Gooberlx2

Lifer
May 4, 2001
15,381
6
91
Fails if a directory is named alpha.py, then it returns everything listed under that directory.

:hmm: Not everything, but yes, it does match stuff it shouldn't.

sample directory/file structure [ (#) where it should match]:
Code:
/test/temp1/
	./alpha.py/
		./a.py (1)
		./apy
		./atest.py (2)
		./atestpy
		./etest.py (3)
		./etest.txt
	./temp/
		./alpha.py/
			./o.py (4)
			./o.py.txt
			./otesteapy.py (5)
			./otesteapy.txt
			./test.py
		./alphapy/
			./otesteapy.py (6)
			./otesteapy.txt
			./test.py
		./otets.py (7)
		./totest.pypy
	./atest.py (8)
	./ta.py
	./test.txt
	./testa.py

From within temp1, output of find . -type f -regex '.+/[aeiou].*\.py':
(#) with valid match, ** with invalid match
Code:
find . -type f -regex '.+/[aeiou].*\.py'
./atest.py (8)
./alpha.py/atest.py (2)
./alpha.py/etest.py (3)
./alpha.py/a.py (1)
./temp/alphapy/otesteapy.py (6)
./temp/alphapy/test.py **
./temp/alpha.py/otesteapy.py (5)
./temp/alpha.py/o.py (4)
./temp/alpha.py/test.py **
./temp/otets.py (7)

Good exercise. Maybe someone better versed with regex can explain how to filter for such an issue.

OP could also just use multiple -name parameters instead, like the following, which gives a properly matched set:
Code:
> find . -type f /( -name 'a*.py' -o -name 'e*.py' -o -name 'i*.py' -o -name 'o*.py' -o -name 'u*.py' -o /)

./atest.py (8)
./alpha.py/atest.py (2)
./alpha.py/etest.py (3)
./alpha.py/a.py (1)
./temp/alphapy/otesteapy.py (6)
./temp/alpha.py/otesteapy.py (5)
./temp/alpha.py/o.py (4)
./temp/otets.py (7)
 
Last edited:

LCTSI

Member
Aug 17, 2010
93
0
66
gooberlx2 I will tinker with it at work a bit on Tuesday and let you know.

I know that past the first / you will need to negate that / with [^/]... find from GNU Findutils by default uses emacs regex so it throws me off as I'm used to perl regex or awk's regex. (Its regex engine can be selected with a runtime option though)
 

LCTSI

Member
Aug 17, 2010
93
0
66
So I've screwed with it some and the negation didn't work the way I thought it would... If you say [^/] all you're saying is "any character that is not /" ... so you'd have to do a negative lookahead, or a lookbehind assertion.

I'm going to go through the GNU findutils regex engines and see which ones do negative lookahead/lookbehind assertions. There may already be an answer on the interwebs!