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

On Unix, I want to ensure all program files in my $DEV directory are correctly terminated with a newline (most will be correct). I want the solution to be fast and short (in that order).

Any solution must properly handle file names with spaces in them (GNU find/xargs can use -print0/xargs -0, but standard find/xargs requires you to escape spaces with \ to stop xargs choking). Two attempts follow. The xargs solution seems a little faster than the pure Perl one. Improvements welcome.

#!/bin/sh find "$DEV" -name '*.cpp' -o -name '*.h' -o -name '*.c' | \ perl -lne'sysopen(Z,$_,0) && sysseek(Z,-1,2) && sysread(Z,$c,1) && clo +se(Z) or warn("$_: $!\n"),next;$c eq "\n" or s/ /\\ /g,print' | \ xargs perl -i -lpe'eof && warn "Fixed $ARGV\n"'
#!/bin/sh perl -MFile::Find -le'find({no_chdir=>1, wanted=>sub{/\.c(?:pp)?$/||/\ +.h$/ or return;sysopen(Z,$_,0) && sysseek(Z,-1,2) && sysread(Z,$c,1) +&& close(Z) or warn("$_: $!\n"),return;$c eq "\n" or push@f,$_}},$ENV +{DEV});@f||exit;@ARGV=@f;$^I="";while(<>){chomp;print;eof && warn "Fi +xed $ARGV\n"}'

Replies are listed 'Best First'.
Re: Properly newline terminate a bunch of files on Unix
by Zaxo (Archbishop) on Jul 11, 2003 at 02:09 UTC

    From the command line, $ perl -pi -e'tr/\015//d' *.{pl,pm,pod,txt} or whatever shell glob you want to pick the files. I think that one's in the FAQs.

    Update: Ah, sorry, I misread,

    #!/usr/bin/perl for (@ARGV) { open my $fh, '+>>', $_ or warn $! and next; seek $fh, 2, -1; if (<$fh> ne "\n") { seek $fh, 2, 0 # seek unneeded on many platforms and print $fh "\n"; print $_, $/; } }
    (untested)

    After Compline,
    Zaxo

      No, I don't want to delete CRs; I just want to make sure that the last line is properly terminated with a LF. Also, I don't want to change a file unless it needs changing and I want to know which files were actually changed.

      Update.

      Ooooh, I like that '+>>' trick. Didn't think of that. Here is a recursive version:

      #!/bin/sh perl -MFile::Find -e'find({no_chdir=>1,wanted=>sub{/\.c(?:pp)?$/||/\.h +$/ or return;open(Z,"+>>",$_) or warn("$_: $!\n"),return;seek(Z,-1,2) +;<Z>eq"\n" or print(Z "\n"),warn("Fixed $_\n");close(Z)}},$ENV{DEV})'