1

I stumbled upon following issue: on a single line, I would like to alter the result of my find command to do a specific cp depending of the files I found, with a destination name that depends of the files returned by the find.

Here are my files,

$  find jcho -name *.data
jcho/category1/001.data
jcho/category2/002.data

and they must be copied to

jcho2/category1_001.data
jcho2/category2_002.data

I tried this,

$ find . -name *.data \
-exec cp  {} ` echo {} | sed -re 's/(jcho\/)(category[0-9]*)(\/)(.*data)/jcho2\/\2_\4/' ` \;

but it says it want to copy to same file -- my substitution is not made; got below error:

cp: './jcho/category1/001.data' and './jcho/category1/001.data' are the same file

So I tried something with a subshell to which I give the result of the find. This worked (a little).

find jcho -name *.data -exec sh -c \ 
'f="${0}"; d=$(echo ${f} | sed -re 's/0/2/' ); cp ${f} ${d} ' {} \;

==>

find jcho -name 2*.data
jcho/category1/201.data
jcho/category2/202.data

If I could include / in the sedpattern, my solution would be at hand...

But I get:

find jcho -name *.data -exec sh -c \
'f="${0}"; d=$(echo ${f} | sed -re 's/(jcho\/)(category[0-9]*)(\/)(.*data)/jcho2\/\2_\4/' ); cp ${f} ${d} ' {} \;

-ksh: syntax error: `(' unexpected

... I tried escaping the / with \, with \\... not better.

idem with

find jcho -name *.data -exec sh -c \
'f="${0}"; d=$(echo ${f} | sed -re 's~\([^)]*\)/\([^()]*\)$~\1_\2~' ); cp ${f} ${d} ' {} \;

Thank you for your help!

J. Chomel
  • 8,193
  • 15
  • 41
  • 69
  • This is the same issue than `find -name` with regex pattern and filename replacement using `cp` [link] (http://stackoverflow.com/questions/36396401/find-name-with-regex-pattern-and-filename-replacement-using-cp) Check the answer there. – Jay jargot Apr 04 '16 at 14:22
  • No, it is more specific. I tried answering it differently, but could not. My question ask a solution with **sed** – J. Chomel Apr 04 '16 at 14:26

3 Answers3

1

You can use this sed with an alternative delimiter ~:

find jcho -name '*.data' | 
while read -r f; do cp "$f" "$(echo "$f" | sed 's~\([^)]*\)/\([^()]*\)$~\1_\2~')"; done

With your find output it will give:

jcho/category1_001.data
jcho/category2_002.data
anubhava
  • 761,203
  • 64
  • 569
  • 643
1

The ( and ) need to be escaped to be referenced later.

The / must be escaped with [/] in regex pattern, or with \/ in the replacement part.

You could try the command-line below:

$ find jcho -name \*.data | sed -n '
{
h
s/^\(.*\)[/]\(category[^/]*\)[/]\(.*[.]data\)$/\12\/\2_\3/
H
x
s/\n/ /
p
}' | xargs -L 1 cp

This is working with the example provided but there will be issues if pathnames or filenames contain special chars: \n " ' space.

There are famous pages to read on these topics:

Let see how sed will handle each filename read from the pipe.

0- -n option instructs sed to not print lines except when p is used.

1- jcho/category1/001.data is read from the pipe and it is stored in the pattern buffer.

pattern buffer = jcho/category1/001.data

2- h overwrites the content of the hold buffer with a copy of pattern buffer.

hold buffer = jcho/category1/001.data

3- the first s changes the content of the pattern buffer

pattern buffer = jcho2/category1_001.data

4- H adds pattern buffer to hold buffer

hold buffer = jcho/category1/001.data
jcho2/category1_001.data

5- x exchanges buffers content

pattern buffer = jcho/category1/001.data
jcho2/category1_001.data

6- the last s command replaces '\n' with ' ' in the pattern buffer

pattern buffer = jcho/category1/001.data jcho2/category1_001.data

7- p prints the current pattern buffer.

8- End of the block: return to 1 and work with next filename found.

Jay jargot
  • 2,745
  • 1
  • 11
  • 14
1

probably there is an easier way than this, but with bash basename/dirname combination you can achieve the same

for f in $(find ...); do cp $f $(echo $(dirname $f)"_"$(basename $f)); done

without getting into string manipulation with regex matching.

karakfa
  • 66,216
  • 7
  • 41
  • 56
  • I tried this way but `dirname` is not the right destination. It is a different path I want to copy to: `jcho2`. – J. Chomel Apr 04 '16 at 14:52