MarkTaff.com

  • Increase font size
  • Default font size
  • Decrease font size
Home Software Linux Linux: Wrapper to Copy Files from STDIN

Linux: Wrapper to Copy Files from STDIN

E-mail Print PDF

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.

  1. #!/usr/bin/perl -w
  2.  
  3. # copy - A wrapper around `cp' that can read a list of files to copy, from stdin or an external file.
  4. # Copyright (c) 2009 Mark A. Taff <mark@marktaff.com>
  5. #
  6. # This program is free software: you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation, either version 3 of the License, or
  9. # (at your option) any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  18.  
  19. use Getopt::Long;
  20. use File::Basename;
  21.  
  22.  
  23. # About info
  24. $appName = "copy";
  25. $appDesc = "A wrapper around `cp' that can read a list of files to copy, from stdin or an external file.";
  26. $author = "Mark A. Taff";
  27. $email = "<mark\@marktaff.com>";
  28. $copyright = "Copyright (c) 2009";
  29. $license = "Licensed under the GNU GPL, v3, or any later version.";
  30.  
  31. # Command line args
  32. my $verbose;
  33. my $help;
  34. my $version;
  35. my $src;
  36.  
  37. # Print out usage information
  38. sub help()
  39. {
  40. $help = <<END;
  41. $appName
  42. $appDesc
  43.  
  44. Usage: $appName [options]
  45.  
  46. Options:
  47.   --files-from=[SRC] The file to read the files to be copied from, where SRC
  48.   is the file to read from. If SRC is - files will be read
  49.   from stdin.
  50.   --verbose Operate in verbose mode
  51.   -h, --help Print this help information
  52.   -V, --version Print out version, author, and copyright information
  53.  
  54.   If the destination directory doesn't exist, it will be automatically created. All
  55.   other options are simply passed along to `cp', so see `cp --help' for more information.
  56.  
  57.   To copy a list of files from STDIN to /home/mark/export:
  58.   `copy --files-from=- /home/mark/export`
  59.  
  60.   To copy a list of files from /tmp/export_files to /home/mark/export:
  61.   `copy --files-from=/tmp/export_files /home/mark/export`
  62.  
  63. END
  64. print $help;
  65. } # End sub help()
  66.  
  67.  
  68.  
  69.  
  70. # Print out version information
  71. sub version()
  72. {
  73. $version = <<END;
  74. $appName - $appDesc
  75.   $copyright $author $email
  76.   $license
  77. END
  78. print $version;
  79. } # End sub version()
  80.  
  81.  
  82.  
  83.  
  84. sub buildDest(@)
  85. {
  86. my $dest = shift;
  87. my $src = shift;
  88. my $basename = "";
  89.  
  90. # Create dest directory, if needed
  91. system ("mkdir", "-p", $dest);
  92.  
  93. # Build new destination filename
  94. $basename = `basename "$src"`;
  95. chomp $basename;
  96. if ($dest =~ /\/\Z/gi) {$dest .= $basename;}
  97. else {$dest .= "/" . $basename;}
  98.  
  99. return $dest;
  100. } # End sub buildDest()
  101.  
  102.  
  103.  
  104.  
  105. # Copy a single file
  106. sub copy (@)
  107. {
  108. my @args = @_;
  109. unshift (@args, "cp");
  110.  
  111. # Determine destination arg, and explicitly use full path
  112. for (my $i=2; $i < @args; ++$i)
  113. # NOTE: since we prepended "cp" and the source file to @args earlier,
  114. # know that they are at index 0 & 1, respectively, so we can start at
  115. # index position 2.
  116. {
  117. if (($args[$i] =~ m/\A-t/gi) ||
  118. ($args[$i] =~ m/\A--target/gi))
  119. {$args[$i] = buildDest $args[$i], $args[1];}
  120.  
  121. elsif (($args[$i] =~ /\A\//gi) &&
  122. ($args[$i] ne $args[1]))
  123. {$args[$i] = buildDest $args[$i], $args[1];}
  124. };
  125.  
  126. system(@args) == 0
  127. or warn "system @args failed: $?";
  128. if ($verbose)
  129. {print "Copied $args[1] to $args[2]\n";}
  130. } # End sub copy()
  131.  
  132.  
  133.  
  134. # Split @ARGV into args for `copy' and args for `cp'
  135. @ARGV_COPY = @ARGV;
  136. foreach (@ARGV_COPY)
  137. {
  138. if ($_ eq "--verbose") {push (@copyArgs, $_); next;}
  139. if (($_ eq "--help") || ($_ eq "-h")) {push (@copyArgs, $_); next;}
  140. if (($_ eq "--version") || ($_ eq "-v")) {push (@copyArgs, $_); next;}
  141. if (/--files-from=/gi) {push (@copyArgs, $_); next;}
  142.  
  143. push (@cpArgs, $_);
  144. };
  145.  
  146. # Now reset @ARGV so it only conatins args for `copy'
  147. @ARGV = @copyArgs;
  148.  
  149.  
  150. # Read command line options
  151. GetOptions ( "verbose" => \$verbose,
  152. "help|h" => \$help,
  153. "version|v" => \$version,
  154. "files-from=s" => \$src,
  155. ) or die "Unable to parse command line arguments.";
  156.  
  157.  
  158. # Process command line args
  159. if ($help) {help(); exit 0;}
  160. if ($version) {version(); exit 0;}
  161.  
  162.  
  163.  
  164. if (!($src))
  165. {
  166. # Useless use of copy. Just pass everything to cp
  167. copy( @copyArgs );
  168. exit 0;
  169. }
  170.  
  171. if ($src eq "-")
  172. {
  173. # Read from stdin
  174. while (<STDIN>)
  175. {
  176. my @args = @cpArgs;
  177. unshift (@args, $_);
  178. copy( @args );
  179. }
  180. }
  181. else
  182. {
  183. # Read from a file
  184. open INPUT, "<$src"
  185. or die "Unable to open $src: $?";
  186. while (<INPUT>)
  187. {
  188. my @args = @cpArgs;
  189. unshift (@args, $_);
  190. copy( @args );
  191. }
  192. close INPUT;
  193. }
  194.  
  195. exit 0;
  196.  
GNU GPL, version 3
This software is licensed under the GNU GPL version 3.0 or later.
Last Updated ( Monday, 24 August 2009 13:04 )  

Quotes, Aphorisms & Epigrams

flash•light (flăsh līt′)  n. A highly specialized container, typically used to store dead batteries.