3 #=======================================================================
5 # File ID: f6e12fcc-fa51-11dd-b506-000475e441b9
6 # Reads file names from stdin or files and places them into a directory
7 # structure based on a date in the file name or the modification time.
10 # ©opyleft 2004– Øyvind A. Holm <sunny@sunbase.org>
11 # License: GNU General Public License, see end of file for legal stuff.
12 #=======================================================================
25 our ($opt_c, $opt_d, $opt_h, $opt_l, $opt_L, $opt_m, $opt_o, $opt_O) =
26 ( 0, "", 0, 0, 0, 0, "", 0);
27 our ($opt_q, $opt_s, $opt_S, $opt_v) =
29 getopts
('cd:hlLmo:OqsSv') || die("Option error. Use -h for help.\n");
34 $progname =~ s
#^.*/(.*?)$#$1#;
37 my $id_date = $rcs_id;
38 $id_date =~ s/^.*?\d+ (\d\d\d\d-.*?\d\d:\d\d:\d\d\S+).*/$1/;
40 my $DEFAULT_DIR = "%Y/%m/%d";
41 my $skip_dirs = $opt_s ?
1 : 0;
42 my $skip_files = ($opt_s | $opt_S) ?
1 : 0;
44 if ($opt_o =~ /[^b]/) {
45 die("Uknown value in -o option. Use -h for help.\n");
47 my $replace_if_bigger = ($opt_o =~ /b/) ?
1 : 0;
51 replace_if_bigger = "$replace_if_bigger"
52 skip_files = "$skip_files"
53 skip_dirs = "$skip_dirs"
58 my $simul_str = $skip_files ?
" (simulating)" : "";
62 if ($opt_c + $opt_l + $opt_L > 1) {
63 die("$0: Can’t mix the \"-c\", \"-l\" or \"-L\" options, " .
64 "only one or none allowed.\n");
72 if ($opt_l && ($_ !~ /^\//)) {
73 warn("$_: Pathname is not absolute\n");
77 if (/^(.*)\/([^\
/]+?)$/) {
85 if ($opt_m || $File =~ /^(.*?)\b(\d\d\d\d)-?(\d\d)-?(\d\d)T(\d\d):?(\d\d):?(\d\d)Z\b(.+)/) {
87 my ($Pre, $Year, $Mon, $Day, $Hour, $Min, $Sec, $Rest);
89 ($Pre, $Year, $Mon, $Day, $Hour, $Min, $Sec, $Rest) =
90 ( $1, $2, $3, $4, $5, $6, $7, $8);
92 my $From = "$Path/$File";
97 my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev,
98 $size, $atime, $mtime, $ctime, $blksize, $blocks) =
100 my @TA = gmtime($mtime);
101 ( $Year, $Mon, $Day, $Hour, $Min, $Sec) =
102 ($TA[5]+1900, $TA[4]+1, $TA[3], $TA[2], $TA[1], $TA[0]);
103 $Year = sprintf("%04u", $Year);
104 $Mon = sprintf("%02u", $Mon);
105 $Day = sprintf("%02u", $Day);
106 $Hour = sprintf("%02u", $Hour);
107 $Min = sprintf("%02u", $Min);
108 $Sec = sprintf("%02u", $Sec);
111 my $Dir = length($opt_d) ?
$opt_d : $DEFAULT_DIR;
112 $Dir =~ s/%Y/$Year/g;
115 $Dir =~ s/%H/$Hour/g;
120 my $Dest = "$Dir/$File";
123 if (!$opt_O && -e
$Dest) {
124 if (length($opt_o)) {
125 D
("!\$opt_O && -e \$Dest\n");
126 my ($f_dev, $f_ino, $f_mode, $f_nlink, $f_uid,
127 $f_gid, $f_rdev, $f_size, $f_atime, $f_mtime,
128 $f_ctime, $f_blksize, $f_blocks) = stat($From);
129 my ($d_dev, $d_ino, $d_mode, $d_nlink, $d_uid,
130 $d_gid, $d_rdev, $d_size, $d_atime, $d_mtime,
131 $d_ctime, $d_blksize, $d_blocks) = stat($Dest);
132 D
("f_size = \"$f_size\", d_size = \"$d_size\", " .
133 "replace_if_bigger = $replace_if_bigger\n");
134 if ($replace_if_bigger && ($f_size <= $d_size)) {
135 warn("\"$From\" is not bigger than \"$Dest\", " .
136 "will not overwrite\n");
137 # unlink($From) || warn("$From: Can’t delete file: $!\n");
145 if ($write_ok) { # FIXME
146 D
("Inside \$write_ok\n");
149 mkpath
($Dir, $opt_v ?
1 : 0, 0777) ||
150 die("mkpath(\"$Dir\", 0, 0777): $!");
153 print("Copying \"$From\" to " .
154 "\"$Dest\"$simul_str...");
156 copy
($From, $Dest) ||
157 die("\ncopy(\"$From\", \"$Dest\"): $!");
158 $opt_v && print("OK\n");
161 print("Linking \"$From\" to " .
162 "\"$Dest\"$simul_str...");
164 link($From, $Dest) ||
165 die("\nlink(\"$From\", \"$Dest\"): $!");
166 $opt_v && print("OK\n");
169 print("Symlinking \"$From\" to " .
170 "\"$Dest\"$simul_str...");
172 symlink($From, $Dest) ||
173 die("\nsymlink(\"$From\", " .
175 $opt_v && print("OK\n");
178 print("Moving \"$From\" to " .
179 "\"$Dest\"$simul_str...");
181 move
($From, $Dest) ||
182 die("\nmove(\"$From\", \"$Dest\"): $!");
183 $opt_v && print("OK\n");
187 $opt_q || warn("$Dest: File already exists, will not overwrite\n");
191 $opt_q || warn("Ignoring non-regular file $From\n");
194 warn("$From: File not found\n");
201 chomp(my $Txt = shift);
202 my @call_info = caller;
203 $Debug && print(STDERR
"DEBUG $call_info[2]: $Txt\n");
208 open(FILE
, $File) || die("$File: $!");
211 return (Digest
::MD5
->new->addfile(*FILE
)->hexdigest, " $File\n");
215 # Send the help message to stdout {{{
220 $progname v$VERSION -- $id_date
222 Syntax: $0 [options] [file_with_filenames [...]]
224 The program reads file names from stdin or from the files on the command
225 line and moves or copies the files into a directory structure defined by
226 the user. It can also create soft or hard links if the file system
227 allows it. The file name has to contain a date on the format
231 which is the date specified in UTC.
235 -c Copy files instead of move
236 -d X Place files under directory X
237 Use the following modifiers for subtree layout:
239 %Y Year with four digits
241 %d Day of month (00..31)
245 %% Regular percent sign
247 If the -d option is not specified, "$DEFAULT_DIR" will be used.
250 -l Create symlinks instead of moving or copying files. The file names
251 in the input has to contain an absolute path to prevent creating
252 dead links. File names not starting with "/" will be ignored.
253 -L Create hard links to the files instead of copying or moving.
254 -m Use the file modification time instead of date found in the file
255 name. All files will be affected, not only those with a date in
257 -o X Overwrite file if certain conditions is met:
259 i File has identical data
261 s New file is smaller
268 File is bigger or newer
272 File is smaller or identical
274 (This is a TODO, only the 'b' option is implemented.)
277 -q Be quiet, suppress non-critical messages.
278 -s Simulate, don't really move or copy files.
279 -S Semisimulate. Don’t touch the files, only create the directory
280 structure. Useful for running tests with big amounts of data.
283 These options are likely to change at the moment.
285 Note: Files on the command line will not be moved themselves, but shall
286 contain file names of the relevant files to be moved.
291 find /var/tmp/afvroot | afv_move -vl -d newdir/%Y-%m-%d/%H
292 afv_move -vL /tmp/filenames.txt -d %Y/%Y-%m-%d
294 Made by Øyvind A. Holm <sunny\@sunbase.org>
295 License: GNU General Public License version 2 or later ♥
308 This program is free software; you can redistribute it and/or modify it
309 under the terms of the GNU General Public License as published by the
310 Free Software Foundation; either version 2 of the License, or (at your
311 option) any later version.
313 This program is distributed in the hope that it will be useful, but
314 WITHOUT ANY WARRANTY; without even the implied warranty of
315 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
316 See the GNU General Public License for more details.
318 You should have received a copy of the GNU General Public License along
319 with this program; if not, write to the Free Software Foundation, Inc.,
320 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
324 # vim: set fenc=UTF-8 ft=perl fdm=marker ts=4 sw=4 sts=4 et fo+=w :