Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations derfloh on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

How do I treat STDIN like any file entered on commandline?

Status
Not open for further replies.

mikevh

Programmer
Joined
Apr 23, 2001
Messages
1,033
Location
US
I'm just getting back to Perl after being away for a while
and am now using it on Windows instead of Unix as before.
I've written a Perl script that works like Unix "wc",
because I missed it on Windows, and to brush up my Perl
after the absence. This works fine as long as I enter
file names/globs on the command line, but I'd like it to
be able to read from a pipe, too, as Unix wc does, e.g.,

cat somefilename | wc

Well, on Windows I guess that would be "type somefilename".

I'm including the full code below, but I think the
relevant parts are:

@filenames = @ARGV ? map {glob($_)} @ARGV : STDIN;

and

if (!$FH->open($_)) {
warn qq(Couldn't open "$_" for input\n);
next;
}


If I just use STDIN or *STDIN, I get
"Couldn't open *main::STDIN for input".

If I use \*STDIN, I get
"Couldn't open GLOB(hex) for input".

What am I doing wrong here? Any help much appreciated.
Thanks.

P.S. Of much less importance, I tried to use Getopt::Long
for option handling and it wouldn't allow option
bundling, even though I said Getopt::Long::Configure("bundling"). (Seems to me it used to allow bundling of
single char options by default.) Anybody had any experience
with this?

Code:
#!/bin/perl
##########################################################################
#usage: $routine -[lcwdh] [file(s)]
#Prints count of lines, words, characters in files, like *nix "wc"
#Options:

#	-l count lines in file(s)
#	-w count words in file(s)
#	-c count characters in file(s)
#	(Note: if none of l,w, or c are specified, does all 3!)
#
#	-h print this help and exit
#	-d print debugging output
##########################################################################

use strict;

(my $routine = $0) =~ s#^.*(\\|\/)##; #routine name, minus path

my $debug;

my @filenames;

my $line;

my $line_count;
my $word_count;
my $char_count;

my $line_total = 0;
my $word_total = 0;
my $char_total = 0;

my $line_print;
my $word_print;
my $char_print;

my %opt;
my $show_all;

my $FH;

&process_options;

@filenames = @ARGV ? map {glob($_)} @ARGV : STDIN;
&usage unless @filenames;

&print_heading;

&process_files;

&print_totals if (@filenames > 1);

exit(0);

################################## SUBROUTINES ##############################################

sub process_files {
	
	use FileHandle;

	$FH = FileHandle->new;

	for (@filenames) {
	
		#if (! -T $_) {
		#	warn qq("$_" doesn't exist or isn't a text file\n);
		#	next;
		#}	

		if (!$FH->open($_)) {
			warn qq(Couldn't open "$_" for input\n);
			next;
		}

		$line_count = 0;
		$word_count = 0;
		$char_count = 0;

		while (not($FH->eof)) {
			$line = $FH->getline;
			$line_count++;
			$word_count += split(/\s+/, $line);
		}
		$char_count = $FH->tell;

		&print_detail;

	        $line_total += $line_count;
        	$word_total += $word_count;
	        $char_total += $char_count;

		$FH->close || die qq(Couldn't close "$_\n");
	}
} #end process_files

sub print_heading {
	printf "%9s ", "lines" if $line_print;
	printf "%9s ", "words" if $word_print;
	printf "%9s ", "chars" if $char_print;
	printf "%-s\n", "file";
} #end print_heading

sub print_detail {
	printf "%9d ", $line_count if $line_print;
	printf "%9d ", $word_count if $word_print;
	printf "%9d ", $char_count if $char_print;
	printf "%-s\n", $_;
} #end print_detail

sub print_totals {
	printf "%9d ", $line_total if $line_print;
	printf "%9d ", $word_total if $word_print;
	printf "%9d ", $char_total if $char_print;
	printf "%-s\n", "total";
} #end print_totals

sub usage { 
warn <<ENDUSAGE;

usage: $routine -[lcwdh] [file(s)]
Prints count of lines, words, characters in files, like *nix &quot;wc&quot;

Options:
	-l count lines in file(s)
	-w count words in file(s)
	-c count characters in file(s)
	(Note: if none of l,w, or c are specified, does all 3!)

	-h print this help and exit
	-d print debugging output
ENDUSAGE
exit(1);
} #end usage

sub process_options {

	use Getopt::Std; #can't get Getopt::Long to work with bundling!

	getopts(q(lwcdh), \%opt) || &usage;

	$debug = $opt{d};

	if (!defined($opt{l}) && !defined($opt{w}) && !defined($opt{c})) {
	#if we entered none of the above options, it's the same as entering them all!
		$show_all = 1;
	}

	($line_print, $word_print, $char_print) = map {$_ || $show_all} @opt{qw(l w c)};

	&usage if $opt{h};
} #end process_options


__END__


 
I figured out how to do what I wanted. Wrote an extra
routine to process @ARGV:

sub process_args {

my @OLD_ARGV = @ARGV;
@ARGV = map { glob($_) } @OLD_ARGV;

for (@ARGV) {
warn qq(&quot;$_&quot; is not a text file\n) unless -T $_;
}

@ARGV = grep( -T, @ARGV); #weed out non-text files

#if no arguments and STDIN is not connected to a terminal, assume we're reading from a
#pipe
@ARGV = &quot;-&quot; if (@ARGV == 0 && not(-t STDIN));

$filecount = @ARGV;

} #end process_args

The string &quot;<&STDIN&quot; will also work instead of &quot;-&quot;.
(Learned this from looking at perlopen.)

Had to modify my &quot;process_files&quot; routine:

sub process_files {

while (defined($_ = <>)) {

$line_count++;
$word_count += split(/\s+/);

if (eof) {
$char_count = tell();
&print_detail;

$line_total += $line_count;
$word_total += $word_count;
$char_total += $char_count;

$line_count = 0;
$word_count = 0;
$char_count = 0;
}
}

} #end process_files
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top