Richard
Richard

Reputation: 33

Command Linux to copy files from a certain weekday

I am figuring out a command to copy files that are modified on a Saturday.

find -type f -printf '%Ta\t%p\n'

This way the line starts with the weekday.

When I combine this with a 'egrep' command using a regular expression (starts with "za") it shows only the files which start with "za".

find -type f -printf '%Ta\t%p\n' | egrep "^(za)"

("za" is a Dutch abbreviation for "zaterdag", which means Saturday, This works just fine.

Now I want to copy the files with this command:

find -type f -printf '%Ta\t%p\n' -exec cp 'egrep "^(za)" *' /home/richard/test/ \;

Unfortunately it doesn't work.

Any suggestions?

Upvotes: 1

Views: 216

Answers (2)

tripleee
tripleee

Reputation: 189618

The immediate problem is that -printf and -exec are independent of each other. You want to process the result of -printf to decide whether or not to actually run the -exec part. Also, of course, passing an expression in single quotes simply passes a static string, and does not evaluate the expression in any way.

The immediate fix to the evaluation problem is to use a command substitution instead of single quotes, but the problem that the -printf function's result is not available to the command substitution still remains (and anyway, the command substitution would happen before find runs, not while it runs).

A common workaround would be to pass a shell script snippet to -exec, but that still doesn't expose the -printf function to the -exec part.

find whatever -printf whatever -exec sh -c '
    case $something in za*) cp "$1" "$0"; esac' "$DEST_DIR" {} \;

so we have to figure out a different way to pass the $something here.

(The above uses a cheap trick to pass the value of $DEST_DIR into the subshell so we don't have to export it. The first argument to sh -c ... ends up in $0.)

Here is a somewhat roundabout way to accomplish this. We create a format string which can be passed to sh for evaluation. In order to avoid pesky file names, we print the inode numbers of matching files, then pass those to a second instance of find for performing the actual copying.

find \( -false $(find -type f \
      -printf 'case %Ta in za*) printf "%%s\\n" "-o -inum %i";; esac\n' |
    sh) \) -exec cp -t "$DEST_DIR" \+

Using the inode number means any file name can be processed correctly (including one containing newlines, single or double quotes, etc) but may increase running time significantly, because we need two runs of find. If you have a large directory tree, you will probably want to refactor this for your particular scenario (maybe run only in the current directory, and create a wrapper to run it in every directory you want to examine ... thinking out loud here; not sure it helps actually).

This uses features of GNU find which are not available e.g. in *BSD (including OSX). If you are not on Linux, maybe consider installing the GNU tools.

Upvotes: 2

Sebastian Lenartowicz
Sebastian Lenartowicz

Reputation: 4874

What you can do is a shell expansion. Something like

cp $(find -type f -printf '%Ta\t%p\n' | egrep "^(za)") $DEST_DIR

Assuming that the result of your find and grep is just the filenames (and full paths, at that), this will copy all the files that match your criteria to whatever you set $DEST_DIR to.

EDIT As mentioned in the comments, this won't work if your filenames contain spaces. If that's the case, you can do something like this:

find -type f -printf '%Ta\t%p\n' | egrep "^(za)" | while read file; do cp "$file" $DEST_DIR; done

Upvotes: 0

Related Questions