in reply to Yet another substitution problem

s/// stands for substitution operation, not sed. sed(1) is not synonymous to substitution, even though that may be its most used feature. Could you please update the title of your post to "2 substitution problems"?

/vobs/dirname1////dirname2//dirname3////filename becomes /vobs/dirname1/dirname2/dirname3/filename

I have tried to replace 2 or more "/"'s with a single "/" using

$filename =~ s/\/[\/]+/\//;

but the above statement does nothing

That last statement is false, which you would have noticed if you had printed the file name before & after s/// operation. You would have seen that at least first set of consecutive '/' is squashed to single one. To flatten all the rest, all you need to do is globally replace by using /g flag ...

# Used a delimiter other than '/' to avoid escaping '/'. $p =~ s!//+!/!g;

As for your second problem, the subdirectory needs to be captured to reference in the pattern (and later in substitution) ...

$p = '/vobs/synergy_core_apps/code/../code/personalize/src'; print 'before: ' , $p , "\n"; # Besides the /g flag, used /x in order to make the pattern stand out +. $p =~ s{ ([^/]+) /[.]{2}/ \1 } /$1/gx; print 'after: ' , $p , "\n";

If the paths being cleansed actually exist on the file system, I would rather use &Cwd::realpath.

See also: perlretut, perlre, YAPE::Regex::Explain, perlfaq6, Mastering Regular Expressions, etc.

Replies are listed 'Best First'.
Re^2: 2 sed problems
by AnomalousMonk (Archbishop) on Mar 28, 2009 at 15:23 UTC
    The  s{ ([^/]+) /[.]{2}/ \1 } /$1/gx; regex only handles the 'foo/../foo' case; it does not handle something like 'foo/bar/../../baz'.

    Purely as a mental exercise, a deprecated regex approach to handling repeated return to a parental directory might be something like:

    >perl -wMstrict -le "my $parent = qr{ \.\. / }xms; my $dir = qr{ (?> [^/]+ /) (?<! $parent) }xms; print 'output:'; for my $f (@ARGV) { 1 while $f =~ s{ $dir $parent }{}xmsg; print $f } " /vobs/foo/../foo/bar/me/my/../../me/my/moo /vobs/foo/../bar/me/my/../../ma/mo/moo /vobs/x/y/z/../../../x/y/z/filename /vobs/x/y/z/../../../a/b/c/filename /a/../b/c/d/../../e/f/g/h/i/../../../j/k/l/m/n/o/p/../../../../q output: /vobs/foo/bar/me/my/moo /vobs/bar/ma/mo/moo /vobs/x/y/z/filename /vobs/a/b/c/filename /b/e/f/j/k/l/q
    However, this falls into the class of Stupid Regex Tricks because:
    • I doubt it covers all possible variations of *nix path;
    • Even if it covers all such variations (or the subset thereof that the OPer is interested in), extensive testing would be needed to verify this;
    • It does not handle symlinks (see Re^3: 2 substitution problems below);
    • It is not portable to other file systems;
    • And finally, I'm sure there are a few other objections one could think of given a little time.

    As Amphiaraus said, the preferred solution would be some module like Cwd that would overcome all these objections.

    Update: Added symlink objection to list of regex solution objections per parv.

      Given a path actually exists, problem with a solution not actually changing directories is specified in File::Spec/METHODS ...

      canonpath

      No physical check on the filesystem, but a logical cleanup of a path.

      $cpath = File::Spec->canonpath( $path ) ;

      Note that this does *not* collapse x/../y sections into y. This is by design. If /foo on your system is a symlink to /bar/baz, then /foo/../quux is actually /bar/quux, not /quux as a naive ../-removal would give you. If you want to do this kind of processing, you probably want "Cwd"'s "realpath()" function to actually traverse the filesystem cleaning up paths like this.

      Otherwise, OP should test|improve on AnomalousMonk's and/or graff's solutions.

        Otherwise, OP should test|improve on AnomalousMonk's and/or graff's solutions.
        Actually, the OP should not use those solutions at all – unless it's the solution to use the appropriate function in the Cwd or equivalent module.