zentara has asked for the wisdom of the Perl Monks concerning the following question:

Hi, (this was also posted on nntp.perl.beginners, but I don't expect an answer there). I'm just stumped by this one. It all started when I was trying to work out a backup method of backing up all subdirs of /home/user as individual tarballs. So I wanted to copy everything from the user's top level directory to a temporary backup directory. This included all hidden files, hidden directories, and plain files in /home/user. Well, I did some testing as myself, and all seemed to be going well, then I moved the script to /, and see what happens when root ran it. Things went wrong, root runs the same file from /, and it misses plain files like /home/user/sig. I made a little test case to demonstrate the problem.
The following script is run by root in /home/user: /home/zentara/backup-homex ------------------------------------------------------------------------------------------
#!/usr/bin/perl -w @users=('/home/zentara'); #put all user-root hidden files and hidden dirs into 1 temp dir #this makes it easier to backup second level subdirs foreach $homedir(@users){ opendir DIR, $homedir or warn "Cannot readdir $homedir:$!\n"; @files = grep !/^\.\.?$/, readdir DIR; foreach (@files){push @movem,$_ if -f} foreach (@files){push @movem,$_ if ($_ =~ m!^[.]!)} print "@movem\n"; print "##############################################\n"; } exit;
------------------------------------------------------------------------------------------
When the above script is run as user or as root in /home/zentara the output is good:
x z z1 zz sig sig1 sig2 sigs wxyz .Xmodmap sig.perl .bashrc .fvwm2rc
.bash_history backup-homex .Xdefaults .Xauthority .profile .Xresources
dontfeedtrolls .xinitrc wxyz-root .kc .le .mc .qt .ddd .kde .pan .pfm
.ssh .netscape .designer .babygimp .gftp .gimv .fvwm .java .kde2 .sane
.skel .ssh2 .vifm .wine .xmms .Xmodmap .tkalbum .bashrc .gnome2_private
.tkphone .dosemu .mozilla .gconfd .gnome2 .gphoto .gqview .glabels
.gkrellm .fvwm2rc .acrobat .gnucash .gimp-1.2 .pgaccess .regexp
.ptknotes .nautilus .bash_history .mhwaveedit .glameswap .autozen
.kalyptus .antiword .Xdefaults .gnome-desktop .Xauthority .profile
.jpi_cache .gnome_private .Xresources .xinitrc .pornview .adobe .cedit
.dillo .gconf .foxrc .gnome .gnupg .gtklp .links .tkfax .sweep
.cddbslave .roadmap .rolodex .sylpheed .TinyCA .emacs.d .wine.bak
##############################################
------------------------------------------------------------------------------------------
When the above script is run by root from /, the output misses some plain files, notably sig, but many are missing, seemingly random:
wxyz backup-homex .kc .le .mc .qt .ddd .kde .pan .pfm .ssh .netscape
.designer .babygimp .gftp .gimv .fvwm .java .kde2 .sane .skel .ssh2
.vifm .wine .xmms .Xmodmap .tkalbum .bashrc .gnome2_private .tkphone
.dosemu .mozilla .gconfd .gnome2 .gphoto .gqview .glabels .gkrellm
.fvwm2rc .acrobat .gnucash .gimp-1.2 .pgaccess .regexp .ptknotes
.nautilus .bash_history .mhwaveedit .glameswap .autozen .kalyptus
.antiword .Xdefaults .gnome-desktop .Xauthority .profile .jpi_cache
.gnome_private .Xresources .xinitrc .pornview .adobe .cedit .dillo
.gconf .foxrc .gnome .gnupg .gtklp .links .tkfax .sweep .cddbslave
.roadmap .rolodex .sylpheed .TinyCA .emacs.d .wine.bak
##############################################
Can someone shed some light on why this happens? It has something to do with running it from /. I have got around it by chdiring to /home/user to let root run the script from there, but I really would like to know what is going on. Thanks.

Replies are listed 'Best First'.
Re: losing files when run from /
by Fletch (Bishop) on Jan 16, 2003 at 18:28 UTC

    readdir() Just returns the bare filename itself. Likewise, -f and friends look in the current directory. When you run from /, you're getting filenames from $homedir and then looking for them in /.

    Either prefix each element of @files with $homedir, or chdir( $homedir ) before doing -f (or stat() or open() or . . .).

Re: losing files when run from /
by waswas-fng (Curate) on Jan 16, 2003 at 19:54 UTC
    You can do this with a simple shell script as well. For what you are describing perl may be overkill.
    #!/bin/sh HOMEDIRBASE=/data/home BACKUPDIRBASE=/data/SendTarHere cd $HOMEDIRBASE for DIRS in * do tar -cf ${BACKUPDIRBASE}/${DIRS}.tar $DIRS done
      Thanks, for that shell example, but my script is more complex. After getting the dir list, I do a multi-volume tar of it, gzip them,and then write it off to cdr, using ext2 format for the cdr. I'm going to post it later, after I test it a couple of more times. :-)
Re: losing files when run from /
by Aristotle (Chancellor) on Jan 18, 2003 at 15:15 UTC
    Miscellaneous general commentary:
    • You shouldn't use /^\.\.?$/ to throw away the special directories. $ will disregard a trailing newline, so you'll trip over files named "..\n" and such. The proper pattern would be /\A\.\.?\z/, but two humble eqs will do just as well ($_ ne '.' and $_ ne '..').
    • As already mentioned, the file tests and everything else works off of the current directory, so you need to prepend any path you want to them to be relative to to the filename.
    • You're looping over the filename list three times: once in the grep, then twice in one additional foreach loop per test. A lot of duplicated work.
    • You first throw away the special directories, then every other entry that starts with a dot. Since both special directories start with a dot, why not pull these tests together?
    Try something like this:
    #!/usr/bin/perl -w use File::Spec::Functions qw(catfile); use strict; my @users = qw(/home/zentara); foreach $homedir (@users) { my $dh; opendir $dh, $homedir or warn "Cannot readdir $homedir:$!\n"; my @files = grep /\A[^.]/ and -f catfile($homedir, $_), readdir DIR; print "@files\n" . "#"x47 ." \n"; } exit;

    File::Spec::Functions provides importable versions of File::Spec's crossplatform functions for de/constructing pathnames. These modules are in the Perl core.

    Update: s/readdir DIR/readdir $dh/

    Makeshifts last the longest.

        No, I wanted exactly what I wrote. What was wrong is that I had readdir DIR, which was supposed to be readdir $dh.

        Makeshifts last the longest.