There are times you want to copy a selection of files, potentially from various directories, into a single directory. As you may be aware, the GNU copy command, `cp', will not read a list of such files to be copied from STDIN. The GNU `find' command can be used do this, if you want to filter by the info available from stat(1). But what if you want to filter by other info? In my case I want to filter by audio tags.
I wrote a script that traverses one of my music trees (either flac, mp3, or ogg), and filters the list of songs by artist, album, rating, or dance. The script then writes the filtered list to STDOUT. For example, a selection might be "all songs in flac format by George Strait that are rated a 6 or higher" or "all mp3 songs rated 6 or higher". To copy this selection of songs to a single directory, I wrote a wrapper for the `cp' command.
The wrapper command `copy' will read a list of file urls from STDIN or from a file. The script then executes a separate `cp' command for each file, passing along any parameters to the `cp' command. Just copy & paste this code into /usr/local/bin/copy, make it executable, and enjoy.
If you don't read perl, just run `copy --help' for help after you install the script.
#!/usr/bin/perl -w # copy - A wrapper around `cp' that can read a list of files to copy, from stdin or an external file. # Copyright (c) 2009 Mark A. Taff <mark@marktaff.com> # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. use Getopt::Long; use File::Basename; # About info $appName = "copy"; $appDesc = "A wrapper around `cp' that can read a list of files to copy, from stdin or an external file."; $author = "Mark A. Taff"; $email = "<mark\@marktaff.com>"; $copyright = "Copyright (c) 2009"; $license = "Licensed under the GNU GPL, v3, or any later version."; # Command line args my $verbose; my $help; my $version; my $src; # Print out usage information sub help() { $help = <<END; $appName $appDesc Usage: $appName [options] Options: --files-from=[SRC] The file to read the files to be copied from, where SRC is the file to read from. If SRC is - files will be read from stdin. --verbose Operate in verbose mode -h, --help Print this help information -V, --version Print out version, author, and copyright information If the destination directory doesn't exist, it will be automatically created. All other options are simply passed along to `cp', so see `cp --help' for more information. To copy a list of files from STDIN to /home/mark/export: `copy --files-from=- /home/mark/export` To copy a list of files from /tmp/export_files to /home/mark/export: `copy --files-from=/tmp/export_files /home/mark/export` END } # End sub help() # Print out version information sub version() { $version = <<END; $appName - $appDesc $copyright $author $email $license END } # End sub version() sub buildDest(@) { my $basename = ""; # Create dest directory, if needed # Build new destination filename $basename = `basename "$src"`; if ($dest =~ /\/\Z/gi) {$dest .= $basename;} else {$dest .= "/" . $basename;} } # End sub buildDest() # Copy a single file sub copy (@) { my @args = @_; # Determine destination arg, and explicitly use full path for (my $i=2; $i < @args; ++$i) # NOTE: since we prepended "cp" and the source file to @args earlier, # know that they are at index 0 & 1, respectively, so we can start at # index position 2. { if (($args[$i] =~ m/\A-t/gi) || ($args[$i] =~ m/\A--target/gi)) {$args[$i] = buildDest $args[$i], $args[1];} elsif (($args[$i] =~ /\A\//gi) && ($args[$i] ne $args[1])) {$args[$i] = buildDest $args[$i], $args[1];} }; if ($verbose) } # End sub copy() # Split @ARGV into args for `copy' and args for `cp' @ARGV_COPY = @ARGV; foreach (@ARGV_COPY) { }; # Now reset @ARGV so it only conatins args for `copy' @ARGV = @copyArgs; # Read command line options GetOptions ( "verbose" => \$verbose, "help|h" => \$help, "version|v" => \$version, "files-from=s" => \$src, # Process command line args if (!($src)) { # Useless use of copy. Just pass everything to cp copy( @copyArgs ); } if ($src eq "-") { # Read from stdin while (<STDIN>) { my @args = @cpArgs; copy( @args ); } } else { # Read from a file while (<INPUT>) { my @args = @cpArgs; copy( @args ); } close INPUT; }

This software is licensed under the GNU GPL version 3.0 or later.






















