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

Monks, I've got a weird problem with a script I'm writing to produce login batch files for Windows computers connecting to a Samba host running a recent RH Linux.

For the Windows machines to make sense of the files, I have to get the line endings right. If I print a line out with a ^M before the newline it works correctly, but not in a heredoc. The following snippet shows the behaviour (using cat -v to display control characters: this is a direct cut and paste:)

#! /usr/bin/perl -w use strict; open OUT, '>', 'ex1' or die "ex1: $!\n"; # print OUT <<'MULTI'; # makes no difference print OUT <<MULTI; foo^M bar^M MULTI print OUT "rat^M\n"; print OUT "bar^M\n"; close OUT; __PRODUCES__ foo bar rat^M bar^M

Those ^Ms really are control-Ms. I've also tried binmoding the OUT filehandle but it makes no difference. I've never run into this before. Can someone explain to me what's going on? Why aren't the control-Ms appearing in the part of the file that was printed from a heredoc? What's the workaround (other than running a unix2dos script on the resulting file afterwards)? This is perl 5.8.0. Thanks.

update: the various suggestions below do indeed fix the problem, and for that I thank my fellow monks. But I still don't know why the behaviour occurs. And nor is it related only to heredocs. An ordinary "double-quote" string has the same effect. Embed a ^M\n and it will be printed out correctly. But a trailing ^M at the end of a line in a multiline string will be silently ignored. I suppose ysth is on the right track. In this case, perl is too smart for its own good.

Replies are listed 'Best First'.
Re: Can't create DOS line endings under Unix in heredocs
by Paladin (Vicar) on Nov 28, 2003 at 22:53 UTC
    You can use \r or \cM in double quoted strings to create the ^M.
    print OUT <<MULTI; foo\r bar\r MULTI
    Seems to work just fine on my machine here.
Re: Can't create DOS line endings under Unix in heredocs
by thospel (Hermit) on Nov 29, 2003 at 01:05 UTC
    Nowadays perl tends to opens the scripts in binmode, and removes one level of CR (though there is a whole buch of defines which control this, and the exact behaviour has been different at times). That means that *one* literal CR at the end tends to disappear.

    You could solve it by adding two CR's at the end of each line, but it makes you dependent on details of how perl was compiled, not a good idea. Nor is it a good idea to have important characters that tend to become invisible on many editors in many modes.

    So best is to either use the an escape for a double-quotish here document, or use a little bit of code to add a CR just before each line end, as already indicated in the previous answers.

    The right escape to use will be either \r or \cM, depending on wether you mean logical cariage return or a control-M (these are not necessarily always the same if you want to be portable)

    It's also interesting to note that a #!/usr/bin/perl line better not end on CR, since on unix that line is first read by the kernel and it will look for a program perl^M in /usr/bin, which probably doesn't exist. #!/usr/bin/perl -w will work fine though, since now the ^M is in the options and will get ignored.

    update The original question was updated with WHY?

    Skipping all history, it's nowadays a binmode open because it wants no strange transforms when loading bytecode. And the CR strip is so that files that have a CR after each line will work. You get such files because e.g. you created a normal text file on microsoft systems, or you transferred such a file (in binary) to a UNIX system, or you fetched something over some CRLF internet protocol without removing the CR's.

Re: Can't create DOS line endings under Unix in heredocs
by ysth (Canon) on Nov 28, 2003 at 22:57 UTC
    Because perl often is fed dos files even on unix, it tries to come up with the appropriate line endings for the OS it is running under. Save it to a variable and s///:
    $x = <<MULTI; foo bar MULTI $x =~ s/\n/\r\n/g; print OUT $x;
Re: Can't create DOS line endings under Unix in heredocs
by BUU (Prior) on Nov 29, 2003 at 02:25 UTC
    Heres a hack, $\="\r"; throw that at the top of yer perl program and presto, it works!