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

If I try to write:
system("echo -e "\xFF\x08\x01" >> /dev/ttyACM0");
All is good. But when I try to write:
system("echo -e "\xFF\x08\x00" >> /dev/ttyACM0");
It gives me syntax error. Do you guys have any idea why this is happening, and how I can espace this so that the command works? I suspect that the \x00 is a null byte that is terminating the command , thus the error. But how to fix?

Replies are listed 'Best First'.
Re: null byte issue in system command ('\0' in C)
by tye (Sage) on Apr 01, 2015 at 01:19 UTC

    Prior comments were tripped up by your lack of escaping of your (inner) double quotes; so I'll start by restating the problem without that mistake:

    $ perl -e 'system("echo -e \"\xFF\x08\x01\" | od");' 0000000 062455 177440 000410 000012 0000007 $ perl -e 'system("echo -e \"\xFF\x08\x00\" | od");' sh: 1: Syntax error: Unterminated quoted string $

    And the explanation is quite simple. Perl supports "\x00" bytes in strings but many of the C library routines that Perl must call only support "\x00"-terminated strings.

    Even in C, if you pass "echo blah\0blah blah" to system(), what gets passed is more accurately "echo blah\0blah blah\0" and system()/sh stop copying/parsing the string when they hit a terminating "\0" character... the first one, of course. (This is also described in the standard Perl documentation.)

    - tye        

Re: null byte issue in system command
by choroba (Archbishop) on Mar 31, 2015 at 22:41 UTC
    Double quotes nested in double quotes? All is good?
    Backslash found where operator expected at ./1.pl line 5, near ""echo +-e "\" (Missing operator before \?) Backslash found where operator expected at ./1.pl line 5, near "xFF\" Backslash found where operator expected at ./1.pl line 5, near "x08\" String found where operator expected at ./1.pl line 5, near "x01" >> / +dev/..."" syntax error at ./1.pl line 5, near ""echo -e "\" Execution of ./1.pl aborted due to compilation errors. [255]
    لսႽ ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
Re: null byte issue in system command
by mr_mischief (Monsignor) on Mar 31, 2015 at 23:21 UTC

    I can't replicate your failure or your success.

    $ perl -e 'system("echo -e "\xFF\x08\x00" >> /dev/ttyACM0");' Backslash found where operator expected at -e line 1, near ""echo -e " +\" (Missing operator before \?) Backslash found where operator expected at -e line 1, near "xFF\" Backslash found where operator expected at -e line 1, near "x08\" String found where operator expected at -e line 1, near "x00" >> /dev/ +ttyACM0"" syntax error at -e line 1, near ""echo -e "\" Execution of -e aborted due to compilation errors.

    This fails for lack of permissions on my specific system (running as non-root):

    $ perl -e 'system q{echo -e "\xFF\x08\x00" >> /dev/ttyACM0};' sh: /dev/ttyACM0: Permission denied

    Writing to a regular file in my home directory works fine (except this echo does not support the same syntax as yours):

    $ perl -e 'system q{echo -e "\xFF\x08\x00" >> ~/foo };' $ cat ~/foo -e � $

    On Linux:

    # perl -e 'system q{echo -e "\xFF\x08\x00" >> ~/foo };' # cat ~/foo � #

    Multiple versions on differing platforms:

    • This is perl 5, version 18, subversion 2 (v5.18.2) built for darwin-thread-multi-2level
    • This is perl 5, version 14, subversion 4 (v5.14.4) built for x86_64-linux-64int
    • This is perl, v5.10.1 (*) built for x86_64-linux-thread-multi

    I have no idea how that would ever succeed with unescaped double quotes inside double quotes like that on any version. It just isn't valid Perl. How do I post a question effectively? is your friend (thanks jeffa and the SiteDocClan). Perhaps see also:

Re: null byte issue in system command
by afoken (Canon) on Apr 01, 2015 at 08:34 UTC
    system("echo -e "\xFF\x08\x01" >> /dev/ttyACM0");

    Why do you shell out just to write to a file or device? Perl has open, print, close, no need to mess with the shell. Using File::Slurp reduces that to a single line for each write, including error handling:

    use File::Slurp qw( append_file ); if ($foo) { append_file('/dev/ttyACM0',{ err_mode => 'croak', binmode => ':raw +' },"\xFF\x08\x00"); } else { append_file('/dev/ttyACM0',{ err_mode => 'croak', binmode => ':raw +' },"\xFF\x08\x01"); }

    Some other notes:

    • I don't think you need to append to /dev/ttyACM0, it's a tty device that usually is not seekable, so a single ">" in the shell or write_file() in case of File::Slurp should be sufficient.
    • I guess that the code you posted here is not the code you actually wrote. You managed to write something like system("echo -e '\xFF\x08\x00' >> /dev/ttyACM0"), and here things start to go wrong. The string you pass to system contains a NUL character. Perl has no problem with that, but the underlying C API has. C uses the NUL character as end-of-string marker, because C uses strings without length information. For C, you have passed the string "echo -e '".chr(255).chr(8) to system(). No problem for system(), but system passes exactly that to the shell. echo -e followed by a string "in" single quotes, but with the final single quote missing. Hence the syntax error from the shell. Had you used single quotes in perl, the backslashes would have been passed to the shell unmodified, without an embedded NUL character. echo -e, not perl, would have changed \x00 to a NUL character.
    • append_file is ok for one or two "single-shot" commands, but repeatedly calling append_file is inefficient. In that case, use classic open/print/close, perhaps enabling autoflush on the file handle so that control commands don't sit in the file buffers for ages.

    Alexander

    --
    Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)