Peter’s blog ✴ Week 112 ✴ 10 May 2021

THE WEEKLY CHALLENGE
Along the path and up the stairs

The Perl Camel

Task 1

Canonical path

You are given a string path, starting with a slash ‘/’. Write a script to convert the given absolute path to the simplified canonical path.

In a Unix-style file system:

  • A period '.' refers to the current directory
  • A double period '..' refers to the directory up a level
  • Multiple consecutive slashes ('//') are treated as a single slash '/'

The canonical path format:

  • The path starts with a single slash '/'.
  • Any two directories are separated by a single slash '/'.
  • The path does not end with a trailing '/'.
  • The path only contains the directories on the path from the root directory to the target file or directory

Examples


Example 1
Input: "/a/"
Output: "/a"

Example 2
Input: "/a/b//c/"
Output: "/a/b/c"

Example 3
Input: "/a/b/c/../.."
Output: "/a"

Analysis

I took the view that a node name along a path can contain any character except '/', which is close to true for Unix-like environments. Moreover, this is an absolute path, that is, it's the path from the root of the file system, '/', and must therefore start with '/' and not, for example, with '.' or '..'.

So I started with @names = split("/+", $path) and then iterated $x along @names. If $x eq '..' and my (new) path isn't empty then I remove the last /$x from the path, and otherwise I add /$x

And that's it.

I am sorry that the 'Try it' feature is currently working very slowly or not at all owing to some issue with my web hosting provider.

Try it 

Try running the script with any input:



example: /all/cows/eat/stones/../grass

Script


#!/usr/bin/perl

# Blog: http://ccgi.campbellsmiths.force9.co.uk/challenge

use v5.26;    # The Weekly Challenge - 2021-05-10
use utf8;     # Week 112 - task 1 - Canonical path
use warnings; # Peter Campbell Smith
binmode STDOUT, ':utf8';
use Encode;

canonical_path('/a/');
canonical_path('/a/b//c/');
canonical_path('/a/b/c/../..');
canonical_path('/var/challenge/112_1/source/..');
canonical_path('/one/two/three/../../../..');

sub canonical_path {
    
    my ($path, @names, $x);
    
    # initialise
    $path = $_[0];
    say qq[\nInput:  $path];
    
    @names = split("/+", $path);
    $path = '';
    
    # add back components
    for $x (@names) {
        
        # ..
        if ($x eq '..' and $path) {
            $path =~ s|/[^/]+$||;
            
        # file/dir name 
        } elsif ($x) {
            $path .= "/$x";
        }
    }
    
    # empty path
    $path = '/' unless $path;
    
    say qq[Output: $path];
}

13 lines of code
Completed after the closing date and not submitted to GitHub

Output from script


Input:  /a/
Output: /a

Input:  /a/b//c/
Output: /a/b/c

Input:  /a/b/c/../..
Output: /a

Input:  /var/challenge/112_1/source/..
Output: /var/challenge/112_1

Input:  /one/two/three/../../../..
Output: /..

 

Any content of this website which has been created by Peter Campbell Smith is in the public domain