Print filename and last line using awk

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.