0

I'm learning to code, following tutorials to try and fix a problem that's been annoying - to clean up files on macOS so they don't jam OneDrive from working.

I have followed several tutorials and it's very nearly doing what I'd hope.

#!/usr/bin/perl -w
use strict;
use warnings;
use File::Find;

my @argv;
my $dir = $ARGV[0];

find(\&dirRecurs, $dir);

sub dirRecurs{

    if (-f and not 's/^\./' )
    {
        (my $txt = $_) =~ s/^\ | (?=\.)|[\/!#%^?*&()\\]| $//g;
        rename($_, $txt);
    }

}

I expected it to exclude .dotfiles from the renaming process as a result of and not 's/^\./' but it doesn't. If I remove the and not 's/^\./' from the if line, all works as I'd hope, except

I expected \/ in the regex to rename files like more/less.pdf but it doesn't

jhnc
  • 11,310
  • 1
  • 9
  • 26
RedZebra
  • 17
  • 6
  • please clarify: is `more/less.pdf` the name of a file or is it the path to a file (`less.pdf` inside directory `more`) ? – jhnc Mar 31 '19 at 23:58
  • `more/less.pdf` is the filename. I'm amazed that MS Office will let you call a file a name with a slash in it. The substitution regex won't pick up these slashes. The `-f` in the `if` makes sure that there are no path slashes. – RedZebra Apr 01 '19 at 06:16
  • actually, the `-f` ensures you don't rename directories (which presumably could contain slashes and other odd characters too). – jhnc Apr 01 '19 at 06:40
  • don't know if this command will work on a mac but what type of fs is this file on: `mount | fgrep $(realpath "$(df more/less.pdf | awk 'NR==2{print $1}')")` – jhnc Apr 01 '19 at 06:44
  • Sorry, I'm not sure how to answer that! It's MacOS. What finder shows as `more/less.pdf` appears as `more:less.pdf` in terminal `ls`. This is really puzzling me. The files with the forward slash are being created on Windows 7 and Windows 10 or Mac. They come from MS Office applications. – RedZebra Apr 01 '19 at 07:09
  • 1
    [link]https://stackoverflow.com/questions/13298434/colon-appears-as-forward-slash-when-creating-file-name[/link] explains that `/` is really a `:` but finder shows it the wrong way. So I'll remove colons from the names. – RedZebra Apr 01 '19 at 07:34

1 Answers1

0

The line of code in question is:

if (-f and not 's/^\./' )
  • This succeeds when -f is true and 's/^\./' is false
    • -f tests if $_ is a file
    • In a boolean context, 's/^\./' tests if the given string is not null

Clearly any string that has some characters in it is not null, and so the if test always fails.

Looking at the contents of the string, it is clear that the intention was to insert a regular expression test. The one that is required is the m// form ("match"). The one that has been (almost) provided, is a malformed version of s/// (search and replace).

In Perl, regular expressions generally do not appear as strings. The appropriate form is (m is optional):

if (-f and not m/^\./)

As couple of asides:

  • @argv is not used in your code (and is not the same as @ARGV)
  • use find(\&dirRecurs, @ARGV); to search more than one path (provided as arguments to the perl script)
  • You perform rename even if there has been no change. This can be prevented by wrapping in and if:
if ((my $txt = $_) =~ s/^\ | (?=\.)|[\/!#%^?*&()\\]| $//g) {
    rename($_, $txt);
}

# or:

rename($_, $txt)
    if (my $txt = $_) =~ s/^\ | (?=\.)|[\/!#%^?*&()\\]| $//g;
jhnc
  • 11,310
  • 1
  • 9
  • 26
  • Thank you so much, this is a brilliant explanation and is really helpful. – RedZebra Apr 01 '19 at 06:28
  • one other check would be to ensure the new name doesn't clobber an existing file – jhnc Apr 01 '19 at 06:48
  • Good call - thank you. Just tried it: in such a case it deletes the file that's being renamed. So it needs `rename -> tempname -> check against existing files -> if clash append incremental to tempname -> go ahead and rename`. That's my next puzzle. – RedZebra Apr 01 '19 at 07:15