Use find with cp and sed in ksh to copy files to a slightly different location

Hello there wonderful people,

I am running on Solaris 10 and with the following ksh version:

strings /bin/ksh | grep Version | tail -2
@(#)Version M-11/16/88i

Suppose I want to copy files that end in _v2 from underneath /dir1/dir2/save directory to /dir1/dir2. Basically, what I�m looking for is to remove /save out of the path.

So I was thinking of something like this:

find /dir1/dir2/save �type f �name *_v2 �exec cp {} `echo {} | sed 's/save//'` \;

But I get this:

cp: /dir1/dir2/save/app.jar_v2 and /dir1/dir2/save/app.jar_v2 are identical

It looks like putting the output of echo through sed does not work as expected.

Any help is much appreciated.
Thank you!

The quotes in the sed command look somewhat suspicious...

Not sure why they ended up like that, that is not the issue though. I confirm I use the correct quotes. I will try adding the quotes one more time below to see if they show up correct:

sed 's/save//' 

Yep, above looks better, these are the quotes I use.

Edit: I have also changed the quotes in the original post.

Hi,

Can you try this one ?

find /dir1/dir2/save -type f -name *_v2 -exec cp {} `dirname {}` \;

Not tested though.

There are several problems with this: first, the "{}" placeholder for the found filename can only be used ONCE in a single -exec clause.

Second: you are calling poor sed for every file you find. This is overworking the system.

try the following:

find /dir1/dir2/save -type f -name "*_v2" -print |\
while read infile ; do
     outfile="${infile%%/save*}${infile*save}"
     print - cp "$infile" "$outfile"
done

I hope this helps.

bakunin

1 Like

There might be a # missing - try

outfile="${infile%%/save*}${infile#*save}"
2 Likes

Yes, absolutely. Sorry for the typo and thanks for pointing it out.

bakunin

I'm be tempted to tackle the problem like this:

cd /dir1/dir2/save

for file in *_v2
do
   [ -f "$file" ] && cp "$file" ..
done

On GNU systems I'd probably do:

find /dir1/dir2/save -name "*_v2" -print0 | xargs --no-run-if-empty -0 cp -t /dir1/dir2

I went with bakunin's suggestion since there are multiple sub-directories underneath /dir1/dr2/save and I want to copy the files ending _v2 outside of save and keep the directory structure.

bakunin, could you please explain the magic behind this line:

outfile="${infile%%/save*}${infile#*save}"

Thank you!

---------- Post updated at 06:44 AM ---------- Previous update was at 03:43 AM ----------

Is there a way of removing _v2 from the end of the file in the line below?

outfile="${infile%%/save*}${infile#*save}"

How about

outfile=${outfile%_v2}

?

man bash : "Parameter Expansion" is a good reading for this...

1 Like

The following did the trick:

print - cp "$infile" "${outfile%%_v2}"

Thank you!

I don't think the second % is needed as you want to remove one single occurrence only.

Hi RudiC,
You are correct. The commands:

print - cp "$infile" "${outfile%%_v2}"
        and:
print - cp "$infile" "${outfile%_v2}"

produce exactly the same results in this case.

Hi ejianu,
Even though both forms produce the same results in this case, I personally prefer to use the % version (like RudiC suggestion in post #10) instead of the %% version you used in post #11 when there is only one possible match.

The difference between the two cases is apparent when the given pattern can match more than once. For example, the following commands:

x=abc.def.ghi
echo ${x%%.*} ${x%.*}

produce the output:

abc.def abc

because %% removes the longest match for .* from the end of $x and % removes the shortest match for .* from the end of $x . (Note that the patterns used in ${var%%pattern} and ${var%pattern} are filename matching patterns; not basic regular expressions and not extended regular expressions.)

Hope this helps,
Don