Camel
Peter
Peter Campbell Smith

Along the path
and up the stairs

Weekly challenge 112 — 10 May 2021

Week 112: 10 May 2021

Task 1

Task — 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.

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

I completed this challenge after the closing date
and it has not been submitted to GitHub

Output


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