Beefy Boxes and Bandwidth Generously Provided by pair Networks
We don't bite newbies here... much

system(cp filenamewithspaces newplace/newname)

by honeyeater (Initiate)
on May 12, 2009 at 17:04 UTC ( [id://763541] : perlquestion . print w/replies, xml ) Need Help??

honeyeater has asked for the wisdom of the Perl Monks concerning the following question:

Title sums it up. Can't figure out why this is going on, or more accurately how to fix it. Script is just renaming files. There's a system call in it: (system("cp $oldname renamed/$newname"); Generally it works just fine. But some of the oldfilenames have a space in them (e.g. 77524812_M13_F_H6 _.ab1) and the cp command gets me this in response: usage: cp [-R -L | -P] -i | -n -pv src target cp [-R -L | -P] -i | -n -pv src1 ... srcN directory As if I didn't know! Any ideas how to fix this?
  • Comment on system(cp filenamewithspaces newplace/newname)

Replies are listed 'Best First'.
Re: system(cp filenamewithspaces newplace/newname)
by ikegami (Patriarch) on May 12, 2009 at 17:07 UTC

    You didn't convert the file names into shell string literals when you created the shell command. There are two simpler alternatives:

    You can use the multi-arg form of system which passes the arguments to the child without serializing them*:

    system('cp', $oldname, "renamed/$newname") or die("cp: $!/$?\n");

    You can use File::Copy:

    copy($oldname, "renamed/$newname") or die("copy: $!\n");

    * — Serialization will occur in Windows, but Perl will handle quoting and escaping for you (within the limits of Windows's command line abilities).

      Brilliant. Thank you so much.
Re: system(cp filenamewithspaces newplace/newname)
by John M. Dlugosz (Monsignor) on May 12, 2009 at 17:10 UTC
    Either escape out the space or put the whole thing in quotes. On systems I've used (Windows, OS/2) double quotes do the trick. I think you need to do it the way your shell understands it, since it will feed the one argument (or two) to the cp command.

    Or, use the multi-argument form of system that will not call the shell. Put one argument per argument.


      Double quotes on a bourne shell are subject to shell interpolation. Strange filenames will bite you:

      #!/usr/bin/perl -w use strict; die "DON'T RUN THIS SCRIPT"; # just in case ... my ($from,$to)=('mostly-harmless.txt','$(rm -rf /)'); system(qq[cp "$from" "$to"]);

      The shell invoked (typically /bin/sh) will see the following command line:

      cp "mostly-harmless.txt" "$(rm -rf /)"

      Before executing cp, the shell must first evaluate "rm -rf /", as its stdout should be passed as second argument to cp.

      The best way to use system is not to mess with the shell at all, i.e. to use the system LIST variant or a perl build-in. In this case, CPAN has File::Copy which handles file copying inside perl, automatically using the most efficient implementation available (system API if present, fall back to open-read-write-close), and without any quoting problems or slow sub-process invokations.


      Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
        Abigail-II has a video (presentation from a conference) where he promotes using cp not File::Copy. Personally, I think a better solution is to fix the nuances in File::Copy that he identified.