On Fri, Jan 13, 2023 at 10:39:02PM +1100, Les Kitchen wrote:
> I'd do something like:
>
> find /Dir1 -type f | perl -lne '$o=$_; s/\.junk\././; print("mv -i $o $_") if 
> $_ ne $o;'

This is quite dangerous, for several reasons.  To start with, there's no
protection against renaming files over existing files with the same target
name.

It also doesn't distinguish between .junk. in a directory name vs in a file
name - it will just modify the first instance of ".junk." it sees in the
pathname. e.g. "./Dir1/My.junk.dir/my.junk.file.txt".  Probably not a problem
in practice, but something to be aware of.

Worse, it will break if any filenames contain whitespace characters (newlines,
tabs, spaces, etc - all of which are completely valid in filenames - the ONLY
characters guaranteed NOT to be in a pathname are / and NUL).

And because you're NOT quoting the filenames in your print statement, it
will also break if any filenames contains shell metacharacters like ; & > <
etc when the output is piped into sh. A simple fix might appear to be to use
single-quotes in the print statement - e.g. print("mv -i '$o' '$_'") - but
even this will break if a filename contains a single-quote character. Similar
for escaped double-quotes.

Shell can be very dangerous if you don't quote your arguments properly.
Consider, for example, what would happen if there happened to be a file called
";rm --no-preserve-root -rf /;" (or ";sudo rm ....;") under /Dir1.  That's
a fairly extreme example of an obviously malicious filename, but there are
plenty of legitimate, seemingly innocuous filenames that WILL cause problems
if passed unquoted to the shell.

Whitespace and quoting issues in shell are well-known and long-standing,
and pretty much inherent to the way the shell parses its command line - the
subject of many FAQs and security advisories.

It's unfortunately very easy to improperly quote filenames - it's far harder
to do correctly and 100% safely than it seems at first glance.

For safety, if you were to DIY it with a command like yours above (there are
far better alternatives), you should use -print0 with find and the -0 option
with perl.

In fact, you should use NUL as the separator with ANY program dealing with
arbitrary filenames on stdin - most standard tools these days have -0 (or
-z or -Z) options for using NUL as the separator, including most of GNU
coreutils etc (head, tail, cut, sort, grep, sed, etc. For awk, you can use
BEGIN {RS="\0"} or similar).

Also:

1. perl has a built-in rename function, there's no need to fork mv (which
would be extremely slow if there are lots of files to rename).  And perl
isn't shell, so doesn't have problems with unquoted whitespace or shell
metacharacters in the filenames.  Still doesn't protect against clobbering
existing filenames without some extra code, though:

$ perldoc -f rename
    rename OLDNAME,NEWNAME
            Changes the name of a file; an existing file NEWNAME will be
            clobbered. Returns true for success; on failure returns false
            and sets $!.

            Behavior of this function varies wildly depending on your system
            implementation. For example, it will usually not work across
            file system boundaries, even though the system *mv* command
            sometimes compensates for this. Other restrictions include
            whether it works on directories, open files, or pre-existing
            files. Check perlport and either the rename(2) manpage or
            equivalent system documentation for details.

            For a platform independent "move" function look at the
            File::Copy module.

            Portability issues: "rename" in perlport.

2. Even better, a perl rename utility (aka file-rename, perl-rename, prename,
etc as mentioned in my previous message in this thread) already exists and
won't overwrite existing files unless you force it to with the -f option.

It also distinguishes between directories and file names (by default, it will
only rename the filename portion of a pathname unless you use the --path or
--fullpath option).  It can take filenames from stdin (and has a -0 option for
NUL-separated filenames) or as command-line args (e.g. with 'find ... -exec
rename .... {} +')

craig
_______________________________________________
luv-main mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to