How to append timestamp in the filenames using find?

Hi,

How to change the filenames with timestamp in sub folders
I have the following code to select the records.

find . -type f -name '*pqr*' -ctime 1 -print

The following is the example

app_root_dir="/`echo $ScriptDir | cut -d'/' -f2`"

$app_root_dir/../BadFiles directory
uvw.bad
xyz.bad
mno.bad

$app_root_dir/../TgtFiles directory
uvw.out
xyz.out
mno.out

I like to rename those files with ..

$app_root_dir/../BadFiles directory
uvw.20130712163028.bad
xyz.20130712163028.bad
mno.20130712163028.bad

$app_root_dir/../TgtFiles directory
uvw.20130712163028.out
xyz.20130712163028.out
mno.20130712163028.out

I already have dt=`date +%Y%m%d%H%M%S` 

I have to rename the filenames as in each environment my path(folder name) differs.

Appreciate advice on other commands/options to use.

First off, is suggest you do away with these backticks. They really, really, REALLY should not be used any more. Use $(command) instead of `command` .

About your question: use the "-exec" clause of "find", which uses "{}" as a placeholder for the found file. For instance:

find . -type f -name '*pqr*' -ctime 1 -exec echo "== {} ==" \;

Which would work like "-print" but surround the filenames with double equal signs.

Use this to pass the filename to a small script, which takes the filename as parameter, adds the timestamp and does a "mv" operation:

timestamp="$(date +%Y%m%d%H%M%S)"
find . -type f -name '*pqr*' -ctime 1 -exec /path/to/script "{}" $timestamp \;

where "script" could look like:

#! /bin/sh
fSrc="$1"
chTimeStamp="$2"
fTgt="${fSrc#.*}${chTimeStamp}${fSrc%*.}"

mv "$fSrc" "$fTgt"

exit $?

I hope this helps.

bakunin

@bakunin

Your code gives me the following result

/<path>/uvw.out20130715101359/<path>/uvw.out  and not
/<path>/uvw.20130715101359.out

I could able to google to change it in one line.

echo "mv ${file} `echo \"$file\" | sed \"s/\(.*\)\./\1\.${dt}./\"`"

The echo above works fine. So I changed it to the following

dt="$(date +%Y%m%d%H%M%S)"
for file in $(find /app_root_dir -type f -name '*test*' -size 72c)
do
  echo "mv ${file} `echo \"$file\" | sed \"s/\(.*\)\./\1\.${dt}./\"`"
  mv ${file} `echo $file | sed "s/\(.*\)\./\1\.${dt}./`
done

I like to use the above mv command along with sed in find itself.

Thanks

Then your input is different from the example you gave, which doesn't have absolute paths at all ("find . ..."). I suggest you consult the man page of "ksh" or "bash" about "variable expansion" and modify my code accordingly to get the results you want.

Your solution is quite inefficient as well as inherently dangerous, because:

This will not work once the path name of the file contains spaces or more than one period character, both perfectly legal possibilities. In addition, the backticks are deprecated and the whole construction with an external program ("sed") which is fed another process ("echo ...|") will consume 2 fork()s for every single file (and the backticks a 3rd fork()), which is quite taxing in terms of resources: memory and CPU time.

This adds even more external calls and the expansion in the subshell ("$(...)") at the head of the for-loop will break once the number of files reach a certain limit. The reason being the way the shell evaluates input lines: subshell substitutions like yours are first executed in a subshell, then their result (=output on stdout) is replaced for them. If your "find" creates a long list you eventually hit the maximum number of characters for input lines, which is a kernel constant (MAX_LIN) defined in "/usr/sys/include/limits.h".

I suggest replacing the for-loop with a while-loop and a pipeline if you insist doing it this way, better yet, do it like i suggested.

I hope this helps.

bakunin

1 Like