Wednesday, July 27, 2016

Linux: Zipping Directories into Zipped Files Recursively

Here's a perl script to do it. The idea is from using find2perl, which can construct how perl would do Unix/Linux find command. Using it as a base, a simple script can be very powerful. Note the use of $prune. This is equivalent to find's -prune option. It is used to stop traversing below the current directory.

  1. #!/usr/bin/perl -w
  2. #
  3. # sqzfps.pl -- compress FPS batch files
  4. #
  5. # CJKim, 27-Jul-2016, Created
  6. #

  7. use strict;
  8. use File::Find ();
  9. use vars qw/*name *dir *prune/;
  10. *name   = *File::Find::name;
  11. *dir    = *File::Find::dir;
  12. *prune  = *File::Find::prune;

  13. my $topdir = 'some-top-directory';
  14. my $aging  = 90; # 365;
  15. my $cputhr = 50;
  16. my $cpuchk = 50;
  17. my $dircnt = 0;
  18. $| = 1;

  19. sub cpu_ok
  20. {
  21.     $dircnt++;
  22.     if ($dircnt > $cpuchk) {
  23.         $dircnt = 0;
  24.         while (1) {
  25.             open CPU, 'vmstat 1 2 | awk \'{if (++c == 4) print $15;}\' |';
  26.             my $idle = <CPU>;
  27.             close CPU;
  28.             $idle += 0;
  29.             last if $idle > $cputhr;
  30.             print "Sleeping for 30 seconds since CPU is at $idle% idle...\n";
  31.             sleep 30;
  32.         }
  33.     }
  34.     return 1;
  35. }

  36. sub wanted
  37. {
  38.     # $_ is the file name only
  39.     # $name is the full path
  40.     # $dir is just the directory name
  41.     # $prune may be set to 1 to stop traversing below the current dir

  42.     my ($dev,$ino,$mode,$nlink,$uid,$gid);

  43.     (($dev,$ino,$mode,$nlink,$uid,$gid) = lstat($_))
  44.     && (-d _)  # directory only
  45.     && (int(-M _) > $aging)  # so many days old
  46.     && ($_ =~ /^\d{16}FF$/)  # fits the name pattern
  47.     && ($prune = 1)  # stop traversing after this one
  48.     && cpu_ok()  # check if we have enough cpu available
  49.     && system "(echo 'Processing $_'; cd $name && zip -m $dir/$_.zip *.* && cd / && rmdir $name)"
  50.     ;
  51. }


  52. # traverse the filesystem
  53. File::Find::find({wanted => \&wanted}, $topdir);



Running the Same Command Periodically and Watch the Output

Have you ever find yourself typing the same command over and over again just to see what's been changing? On a standard Linux, there is a little-known utility called "watch". It can run a command repeatedly every so many seconds and shows you the output. But it goes one step further by showing the differences between the last run and current by highlighting the differences. Yes, it's a clunky command line tool but does a beautiful job of saving a lot of typing and figuring out what changed. See the man pages for watch(1) for the detail and the option flags.

I often used it with SQL*Plus to see the changes in database. For example, you can have a SQL statement in a file called, say, count.sql. You can invoke it using SQL*Plus but you will see the output only one time. To see the output periodically, you can invoke it with watch(1), e.g.

watch -d sqlplus scott/tiger@xe @count

Don't forget to have "EXIT" as the last command in count.sql so that sqlplus would exit after executing the SQL statements.

Cloning a Directory Tree in Linux/Unix

Here's an old command that clones an entire directory tree in Unix or Linux. So far, the command appears to work on all variants of Unix (HP-UX, Solaris, AIX, RedHat, Ubuntu, CentOS, and Fedora):

    $ cd to_the_directory_to_be_cloned  
    $ find . -depth -print | cpio -pdv destination_directory  

While the above command clones all files, since find(1) offers many options, you can clone selected files. For example, if you wish to clone only java programs, specify the option "-name '*.java'" to do so.