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

I'm constructing a string which would represent a user's home directory, if it were used at a shell. Is there a way of having perl expand the string from ~user to /home/user? Or a unix command I could execute for the same result?

Replies are listed 'Best First'.
Re: ~username expansion
by ysth (Canon) on Nov 01, 2004 at 02:09 UTC
    Try just glob("~user"); on newer perls this will use File::Glob, but AFAIK it should also work on older perls. (Doesn't work with win32 perl, though glob("~") does.)
      This worked perfectly and was exactly what I was after. Thanks!
Re: ~username expansion
by atcroft (Abbot) on Nov 01, 2004 at 01:21 UTC

    The following is an example of how I have done it in the past:

    #!/usr/bin/perl -w use strict; use File::Glob qw(:glob); my $dirstring = q(~user); my $newdirstring = File::Glob::bsd_glob( $dirstring, GLOB_TILDE | GLOB_ERR ); print <<RESULTS; Old directory string: $dirstring New directory string: $newdirstring RESULTS

    Hope that helps.

      Sadly, it appears I don't have File::Glob. It's a fairly old Perl installation, and I've noticed deficiencies fairly frequently.

      Thanks anyway for your input.

        I'm not familiar with File::Glob but perl does have a built in glob which I have used it in the past.

        Also I just looked at the docs for File::Glob and it looks like bsd_glob allows for flags which the built in glob does not. However the perldoc for glob notes this:
        Beginning with v5.6.0, this operator is implemented using the standard "File::Glob" extension. See File::Glob for details.
        In fact on my system (Perl v5.8.0, Slackware 9.1) glob matches the tilde in my username:
        [frink@truth]$perl -e 'print map $_ .= "\n" , glob("~frink/tmp/*");' + /home/frink/tmp/137-03.mp3 /home/frink/tmp/156-03.mp3 /home/frink/tmp/2004-01.pdf /home/frink/tmp/2004-01.xls /home/frink/tmp/audio [frink@truth]$
        grep username: /etc/passwd | cut -d: -f6 perl -le 'print glob "~username"';
Re: ~username expansion
by belg4mit (Prior) on Nov 01, 2004 at 03:49 UTC
Re: ~username expansion
by ikegami (Patriarch) on Nov 01, 2004 at 01:44 UTC
    sub get_home_dir { return $ENV{'HOME'} unless @_; my ($userid) = @_; local *PASSWD; open(PASSWD, '<', '/etc/passwd') or die("..."); my ($record) = grep { $_->[0] eq $userid } map { [ split(':') ] } <PASSWD>; return $record && $record->[5]; }

      You may wish to look at the getpwnam() function to get the directory entry for you, such as in the following example:

      #!/usr/bin/perl -w use strict; ( my $name, my $passwd, my $uid, my $gid, my $quota, my $comment, my $gcos, my $dir, my $shell, my $expire ) = getpwnam('user'); # Insert '(undef)' if variable is undefined. foreach my $x ( \$name, \$passwd, \$uid, \$gid, \$quota, \$comment, \$gcos, \$dir, \$shell, \$expire ) { ${$x} = '(undef)' unless ( defined( ${$x} ) ); } printf <<OUTPUT, $name, $passwd, $uid, $gid, $quota, $comment, $gcos, $dir, + $shell, $expire; getpwnam() OUTPUT: name: %s passwd: %s uid: %s gid: %s quota: %s comment: %s gcos: %s dir: %s shell: %s expire: %s OUTPUT

      According to the docs for the function, a null list would be returned if the user did not exist.

      Hope that helps.

      It's better to use getpwnam rather than reading the password file by hand (for example if the machine uses NIS / YP and you're not on the master machine you'll miss accounts).

Re: ~username expansion
by Kyoichi (Novice) on Nov 01, 2004 at 23:36 UTC
    Perl Cookbook's Recipe 7.3, page 253 Greetings