test-ly-files: Display symlink creation, add `-v` to `ln`
[sunny256-utils.git] / afv_move
blobe9164269dc4c895f1e8e1316889bd22872854e94
1 #!/usr/bin/env perl
3 #=======================================================================
4 # afv_move
5 # File ID: d4b6028a-5d36-11df-8bab-90e6ba3022ac
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.
9 # Character set: UTF-8
10 # ©opyleft 2004– Øyvind A. Holm <sunny@sunbase.org>
11 # License: GNU General Public License, see end of file for legal stuff.
12 #=======================================================================
14 use strict;
15 use warnings;
16 use File::Copy;
17 use File::Path;
18 use Getopt::Std;
20 $| = 1;
22 our ($opt_c, $opt_d, $opt_h, $opt_l, $opt_L, $opt_m, $opt_o, $opt_q) =
23 ( 0, "", 0, 0, 0, 0, 0, 0);
24 our ($opt_s, $opt_S, $opt_v) =
25 ( 0, 0, 0);
26 getopts('cd:hlLmoqsSv') || die("Option error. Use -h for help.\n");
28 my $VERSION = "0.5";
30 our $progname = $0;
31 $progname =~ s#^.*/(.*?)$#$1#;
33 my $DEFAULT_DIR = "%Y/%m/%d";
34 my $skip_dirs = $opt_s ? 1 : 0;
35 my $skip_files = ($opt_s | $opt_S) ? 1 : 0;
37 my $simul_str = $skip_files ? " (simulating)" : "";
39 $opt_h && usage(0);
41 if ($opt_c + $opt_l + $opt_L > 1) {
42 die("$0: Can’t mix the \"-c\", \"-l\" or \"-L\" options, " .
43 "only one or none allowed.\n");
46 LOOP: while (<>) {
47 my ($Path, $File) =
48 ("", "" );
50 chomp();
51 if ($opt_l && ($_ !~ /^\//)) {
52 warn("$_: Pathname is not absolute\n");
53 next LOOP;
55 if (/\//) {
56 if (/^(.*)\/([^\/]+?)$/) {
57 ($Path, $File) =
58 ($1, $2 );
60 } else {
61 $Path = ".";
62 $File = $_;
64 if ($opt_m || $File =~ /^(.*?)\b(\d\d\d\d)-?(\d\d)-?(\d\d)T(\d\d):?(\d\d):?(\d\d)Z\b(.+)/) {
65 # {{{
66 my ($Pre, $Year, $Mon, $Day, $Hour, $Min, $Sec, $Rest);
67 unless ($opt_m) {
68 ($Pre, $Year, $Mon, $Day, $Hour, $Min, $Sec, $Rest) =
69 ( $1, $2, $3, $4, $5, $6, $7, $8);
71 my $From = "$Path/$File";
72 if (-e $From) {
73 if (-f $From) {
74 # {{{
75 if ($opt_m) {
76 my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev,
77 $size, $atime, $mtime, $ctime, $blksize, $blocks) =
78 stat($From);
79 my @TA = gmtime($mtime);
80 ( $Year, $Mon, $Day, $Hour, $Min, $Sec) =
81 ($TA[5]+1900, $TA[4]+1, $TA[3], $TA[2], $TA[1], $TA[0]);
82 $Year = sprintf("%04u", $Year);
83 $Mon = sprintf("%02u", $Mon);
84 $Day = sprintf("%02u", $Day);
85 $Hour = sprintf("%02u", $Hour);
86 $Min = sprintf("%02u", $Min);
87 $Sec = sprintf("%02u", $Sec);
90 my $Dir = length($opt_d) ? $opt_d : $DEFAULT_DIR;
91 $Dir =~ s/%Y/$Year/g;
92 $Dir =~ s/%m/$Mon/g;
93 $Dir =~ s/%d/$Day/g;
94 $Dir =~ s/%H/$Hour/g;
95 $Dir =~ s/%M/$Min/g;
96 $Dir =~ s/%S/$Sec/g;
97 $Dir =~ s/%%/%/g;
99 my $Dest = "$Dir/$File";
101 if (!$opt_o && -e $Dest) {
102 $opt_q || warn("$Dest: File already exists, will not overwrite\n");
103 } else {
104 # {{{
105 $skip_dirs ||
106 -d $Dir ||
107 mkpath($Dir, $opt_v ? 1 : 0, 0777) ||
108 die("mkpath(\"$Dir\", 0, 0777): $!");
109 if ($opt_c) {
110 $opt_v &&
111 print("Copying \"$From\" to " .
112 "\"$Dest\"$simul_str...");
113 $skip_files ||
114 copy($From, $Dest) ||
115 die("\ncopy(\"$From\", \"$Dest\"): $!");
116 $opt_v && print("OK\n");
117 } elsif ($opt_L) {
118 $opt_v &&
119 print("Linking \"$From\" to " .
120 "\"$Dest\"$simul_str...");
121 $skip_files ||
122 link($From, $Dest) ||
123 die("\nlink(\"$From\", \"$Dest\"): $!");
124 $opt_v && print("OK\n");
125 } elsif ($opt_l) {
126 $opt_v &&
127 print("Symlinking \"$From\" to " .
128 "\"$Dest\"$simul_str...");
129 $skip_files ||
130 symlink($From, $Dest) ||
131 die("\nsymlink(\"$From\", " .
132 "\"$Dest\"): $!");
133 $opt_v && print("OK\n");
134 } else {
135 $opt_v &&
136 print("Moving \"$From\" to " .
137 "\"$Dest\"$simul_str...");
138 $skip_files ||
139 move($From, $Dest) ||
140 die("\nmove(\"$From\", \"$Dest\"): $!");
141 $opt_v && print("OK\n");
143 # }}}
145 # }}}
146 } else {
147 $opt_q || warn("Ignoring non-regular file $From\n");
149 } else {
150 warn("$From: File not found\n");
152 # }}}
156 sub usage {
157 # Send the help message to stdout {{{
158 my $Retval = shift;
160 print(<<END);
162 $progname v$VERSION
164 Syntax: $0 [options] [file_with_filenames [...]]
166 The program reads file names from stdin or from the files on the command
167 line and moves or copies the files into a directory structure defined by
168 the user. It can also create soft or hard links if the file system
169 allows it. The file name has to contain a date on the format
171 yyyymmddThhmmssZ
173 which is the date specified in UTC.
175 Options:
177 -c Copy files instead of move
178 -d X Place files under directory X
179 Use the following modifiers for subtree layout:
181 %Y Year with four digits
182 %m Month (00..12)
183 %d Day of month (00..31)
184 %H Hour (00..23)
185 %M Minutes (00..59)
186 %S Seconds (00..61)
187 %% Regular percent sign
189 If the -d option is not specified, "$DEFAULT_DIR" will be used.
191 -h Show this help.
192 -l Create symlinks instead of moving or copying files. The file names
193 in the input has to contain an absolute path to prevent creating
194 dead links. File names not starting with "/" will be ignored.
195 -L Create hard links to the files instead of copying or moving.
196 -m Use the file modification time instead of date found in the file
197 name. All files will be affected, not only those with a date in
198 the file name.
199 -o Overwrite existing files
200 -q Be quiet, suppress non-critical messages.
201 -s Simulate, don't really move or copy files.
202 -S Semisimulate. Don’t touch the files, only create the directory
203 structure. Useful for running tests with big amounts of data.
204 -v Verbose execution
206 These options are likely to change at the moment.
208 Note: Files on the command line will not be moved themselves, but shall
209 contain file names of the relevant files to be moved.
211 Examples:
213 ls | afv_move -v
214 find /var/tmp/afvroot | afv_move -vl -d newdir/%Y-%m-%d/%H
215 afv_move -vL /tmp/filenames.txt -d %Y/%Y-%m-%d
217 Made by Øyvind A. Holm <sunny\@sunbase.org>
218 License: GNU General Public License version 2 or later ♥
221 exit($Retval);
222 # }}}
223 } # usage()
225 __END__
227 =pod
229 =head1 LICENCE
231 This program is free software; you can redistribute it and/or modify it
232 under the terms of the GNU General Public License as published by the
233 Free Software Foundation; either version 2 of the License, or (at your
234 option) any later version.
236 This program is distributed in the hope that it will be useful, but
237 WITHOUT ANY WARRANTY; without even the implied warranty of
238 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
239 See the GNU General Public License for more details.
241 You should have received a copy of the GNU General Public License along
242 with this program; if not, write to the Free Software Foundation, Inc.,
243 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
245 =cut
247 # vim: set fenc=UTF-8 ft=perl fdm=marker ts=4 sw=4 sts=4 et fo+=w :
248 # End of file afv_move