OK you're right, my bad.. So yes the example on that page is incorrect, in the sense that it will not work with every awk..
Hi.
I would probably use perl, such as in this example. The steps to identify the missing NEWLINE are done in perhaps excruciating detail:
#!/usr/bin/env bash
# @(#) s1 Driver for perl detector.
# Utility functions: print-as-echo, print-line-with-visual-space, debug.
# export PATH="/usr/local/bin:/usr/bin:/bin"
pe() { for _i;do printf "%s" "$_i";done; printf "\n"; }
pl() { pe;pe "-----" ;pe "$*"; }
db() { ( printf " db, ";for _i;do printf "%s" "$_i";done;printf "\n" ) >&2 ; }
db() { : ; }
C=$HOME/bin/context && [ -f $C ] && $C od perl
pl " Simple perl NEWLINE omission test code:"
cat p1
LIST="first.sh middle.sh last.sh"
rm -f $LIST
./create
for i in $LIST
do
pl " Content of file $i from od:"
od -bc $i
./p1 $i
done
exit 0
producing:
% ./s1
Environment: LC_ALL = C, LANG = C
(Versions displayed with local utility "version")
OS, ker|rel, machine: Linux, 2.6.26-2-amd64, x86_64
Distribution : Debian GNU/Linux 5.0.8 (lenny)
bash GNU bash 3.2.39
od (GNU coreutils) 6.10
perl 5.10.0
-----
Simple perl NEWLINE omission test code:
#!/usr/bin/env perl
# @(#) p1 Demonstrate detection of missing NEWLINE at last line.
use strict;
use warnings;
my ($debug);
$debug = 1;
$debug = 0;
my ( $a, $b, $file, $f );
foreach $file (@ARGV) {
if ( !open( $f, "<", $file ) ) {
print STDERR " Cannot open file $file, skipping.\n";
next;
}
while (<$f>) {
$a = $_;
}
$b = substr( $a, -1, 1 );
print " Last character of last line of file $file is :$b:\n" if $debug;
if ( $b ne "\n" ) {
print " File $file has no NEWLINE on last line.\n";
}
}
exit 0;
-----
Content of file first.sh from od:
0000000 143 165 164 012 157 146 146
c u t \n o f f
0000007
File first.sh has no NEWLINE on last line.
-----
Content of file middle.sh from od:
0000000 167 151 156 144 157 167 163 015 012 163 164 171 154 145 015 012
w i n d o w s \r \n s t y l e \r \n
0000020
-----
Content of file last.sh from od:
0000000 154 141 163 164 012 154 151 156 145 012
l a s t \n l i n e \n
0000012
See man pages and perldoc for details.
Best wishes ... cheers, drl
Here's a very tight AND (hopefully) POSIX compliant version:
for i in *.sh; do echo "<#>"| cat $i - |awk -vfn="$i" '/.<#>/ {gsub("<#>",""); print fn, $0}'; done
a.sh apple
You need to find a "sentinel" string that doesn't interfere with your text.
Here is another one:
find . -type f -name "*.sh" |
while read f; do
[ $(wc -l < "$f") -ne $(grep -c '' "$f") ] && echo "$f"
done
for i in *.sh; do
[ -s "$i" ] && [ $(tail -1 $i | wc -l) = 0 ] && echo "$i has a missing '\n'"
done
Hi RudiC!
Thanks for posting this. As long as an incomplete line at the end of a file is no longer than {LINE_MAX}-4 bytes, this should work on any POSIX compliant system. This limit shouldn't be a problem given the way most of us write shell scripts and the submitter wanted this to process files matching the pattern *.sh.
I don't think we need to worry about this for this project, but note that the shell has no line length limit (other than available resources) when reading shell scripts. If a *.sh file has a valid shell script that contains one or more lines consisting of more than {LINE_MAX} bytes, the awk results would still be unspecified.
---------- Post updated at 15:36 ---------- Previous update was at 15:12 ----------
and
Although both of these suggestions will probably work on many systems, they are not portable solutions. The standards say that the results are unspecified for both tail -n -1 "$i" and grep -c '' "$i" when $i names a non-empty file that does not have a <newline> character as the last byte of the file. (Note also that the 2008 revision of the POSIX standard and the Single UNIX Specification dropped the requirement to treat tail -1 file as a synonym for tail -n -1 file ; this requirement was marked obsolete or deprecated in the 2004 revision of those standards.)
Hi Don,
thank you for pointing this out! Although LINE_MAX may be far away, it may need to be considered, esp. as I was thinking of a looong sentinel code to make it unambiguous and separate it from the file contents.