http://www.perlmonks.org?node_id=1043127

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

Hi,

With MS Windows's cmd.exe shell we can force an exception if any args passed via the command line contain a backslash:
for(@ARGV) { die "Backslash" if $_ =~ /\\/; }
But in the (bash ?) shells that I use on Cygwin and Linux, that works only if the args were placed inside double quotes - ie:
perl script.pl "arg\one"
At least, that's the only way *I* could get the regex to catch the backslash.

So ... if someone does perl script.pl arg\one in one of these shells, is there any way that script.pl can detect the backslash contained in that command line argument ?

Cheers,
Rob

Replies are listed 'Best First'.
Re: [SHELL] Detect backslash in command line args
by choroba (Cardinal) on Jul 08, 2013 at 14:17 UTC
    I am not sure about cmd.exe, but for *nix shells, it is not possible. The backslash is interpreted by the shell running the prompt, which means the script gets the string without the backslash.
    لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
      Yes, that's one of the bigger differences between the MS family and the Unix family. In Unix it's the shell's task to do command line parsing, globbing, etc. while COMMAND.COM and CMD.EXE just call the program and let it do all the rest. So, the answer to the OP: in Unixish systems, there isn't.

        You can probably demonstrate that effect with:

        echo comma\nd with backslash | cat -v

        But the shell will parse the backslash - so in one sense, you don't actually _need_ to detect it, because it isn't there any more by the time your script uses it.

Re: [SHELL] Detect backslash in command line args
by LanX (Saint) on Jul 08, 2013 at 14:30 UTC
    Backslash is an escape character which needs to be escaped ... you might already guess ... with a backslash.

    The shell doesn't act very different to double-quoted strings in Perl.

    UPDATE

    You have a misconception here

    > With MS Windows's cmd.exe shell we can force an exception if any args passed via the command line contain a backslash:

    the same with Unix, just

    > So ... if someone does perl script.pl arg\one in one of these shells, is there any way that script.pl can detect the backslash contained in that command line argument ?

    In your example you are not passing a backslash, just an escaped "o", which is most probably just a simple "o"¹. To be able to pass a backslash you need to double it!!!

    Cheers Rolf

    ( addicted to the Perl Programming Language)

    ¹) yep

    lanx@nc10-ubuntu:~$ perl -le 'print ord(chomp($ARGV[0]))' \o 48 lanx@nc10-ubuntu:~$ perl -le 'print ord(chomp($ARGV[0]))' o 48
Re: [SHELL] Detect backslash in command line args
by Laurent_R (Canon) on Jul 08, 2013 at 17:44 UTC

    You might try this:

    $ perl -e 'die "error: @ARGV[0]" if @ARGV[0] =~ /\\/' foo\\bar error: foo\bar at -e line 1.
      perl -e 'die "error: @ARGV[0]" if @ARGV[0] =~ /\\/' foo\\bar

      That works, but passing 'foo\\bar' is not an error.
      The error I want to catch is when I pass 'foo\bar'.

      Cheers,
      Rob

        Then it is a shell issue, not a Perl issue.

Re: [SHELL] Detect backslash in command line args
by mtmcc (Hermit) on Jul 08, 2013 at 21:20 UTC
    That's a very good problem.

    As others have mentioned, osx/linux strips the command line before passing the arguments to perl, and so I don't know of any way that perl could "see" the unprocessed arguments.

    bash has handy tools that can retrive historical command line arguments. The catch as I see it is that while ~/.bash_history is fully available to your perl script, and contains the unprocessed command line arguments, it only updates when you log out of the shell, and so won't contain the last command issued.

    bash does keep a record of unprocessed command lines in working memory. These can be printed at the command line for example with:

    history | tail -5

    where the last digit determines the number of previous commands to print at the terminal. These are also unprocessed. The problem is that the history command isn't a bash executable, and so doesn't work in the context of a system() call by Perl. Maybe someone knows how to get around this? Would be interested to find the answer.

    Michael
      The problem is that the history command isn't a bash executable, and so doesn't work in the context of a system() call by Perl.

      Might Term::UI::History be of help here ?
      Looks like accessing the shell's history is the only hope.

      Thanks for the confirmations that this isn't trivial anywhere except MS Windows shells (which, I'm assuming, arises because on Windows the backslash is not necessarily an escape).
      I have a perl script that I can use to transfer files from my Windows 7 box to my Ubuntu box. If I do perl get.pl C:/some/file all is fine. I was merely hoping to catch the error when I inadvertently do perl get.pl C:\some\file.
      It's not really such a big deal because I eventually find out about the error anyway - and it's not actually an error that I'm liable to make with great frequency.

      Cheers,
      Rob

        It doesn't arise on Windows, because the OS doesn't process the command line, just hands it straight to perl.

        I'm not certain that Term::UI::History wouldn't work, but because it couldn't be running when the command line is processed by the OS, I can't quite see how it would work.

        If your main concern is that you might ask perl to get a file that doesn't exist, I would use something like:

        die "$fileName not found.\n\n" if !-e $fileName;

        If it's writing to a new file, maybe you could similarly check that the directory exists, before writing to the file ?

        In any case, it's still a good question, and if you do figure out an answer, please let me know!

        Michael