http://www.perlmonks.org?node_id=975991

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

Hi Monks,

I am a beginner to perl scriptting.I have written a script to find the disk usage.When i try to execute the below command from command line I'm getting the required output

 df -h $FILESYSTEM -exec | grep -v "Filesystem" | sed -e "s/%//g"|awk '{print $5}'

But when i try to execute the same in script i'm receving the below exception

bash-2.05$ ./diskusage.pl The Operating System is SunOS Enter the filesystem related to SUN /u001 sh: syntax error at line 2: `|' unexpected
Please guide me.

Replies are listed 'Best First'.
Re: Getting error while executing the script
by blue_cowdawg (Monsignor) on Jun 13, 2012 at 15:33 UTC

    I'm confused... where's the Perl code? I see a shell command line and I see you executing a Perl script called "diskusage.pl" but I don't see the code for that script.

    if this:

    df -h $FILESYSTEM -exec | grep -v "Filesystem" | sed -e "s/%//g"|awk ' +{print $5}'
    is what is tucked inside of diskusage.pl then it has now way of working at all. But I suspect there's more to this... supply the full (or enough of) code you're trying to run.

    simply looking at what you've provided as a command line as a -sh script I'd say that your problem starts with the "-exec" prior to the pipe.

    Based on the cited error you are not even invoking the Perl interpreter at all...


    Peter L. Berghold -- Unix Professional
    Peter -at- Berghold -dot- Net; AOL IM redcowdawg Yahoo IM: blue_cowdawg
      Hi Monk,

      Please find the complete code below

      #!/usr/local/bin/perl #Program to find the disk space and to delete the older files #Checks the type of OS here $OS = `uname -s`; print ("The Operating System is $OS"); if ($OS == SunOS ) { print ("Enter the filesystem related to SUN \n"); $FILESYSTEM = <STDIN>; $DF = `df -h $FILESYSTEM | grep -v "Filesystem" | sed -e "s/%//g"| +awk '{print $5}'`; print ("The file system $ FILESYSTEM usage is $DF % \n"); while ($DF > 50){ print ("The file system $FILESYSTEM reached its maximum capacity $ +DF % and request you to clear the space /n"); print (" Checking the disk usage of $FILESYSTEM and finding the to +p 20 culprits\n"); print ("###############################################"); $DU = `du -sh $FILESYSTEM | sort -rn | head -20`; print ("###############################################"); } else { ("The filesystem size looks good \n"); } }
        if ($OS == SunOS ) {

        The system call to uname returns a string, so you need a string comparison here:

        if ($OS eq 'SunOS') {

        HTH,

        Athanasius <°(((><contra mundum

        Where do I begin with this script? There's all sorts of things wrong with it.

        First off your script should have the following two pragmas right from the start:

        use strict; use warnings;
        this will aid you in troubleshooting your code by forcing you to use proper techniques so far as scoping, etc. etc. etc.

        #Checks the type of OS here $OS = `uname -s`; print ("The Operating System is $OS"); if ($OS == SunOS ) { &< snip1 &<

        You're missing a few things here... A rewrite should look like:

        my $os = `uname -s`; # why capital? chomp $os; # get rid of the EOL character, you'll be glad +you did. printf "Operating system: %s\n",$os; if ( $os eq 'SunOS' ) { # this is where EOL needs to be gone... &< snip! &<

        Next bit of headache:

        $DF = `df -h $FILESYSTEM | grep -v "Filesystem" | sed -e "s/%//g"| +awk '{print $5}'`; print ("The file system $ FILESYSTEM usage is $DF % \n");
        If you are programming in Perl why the h3!! are you invoking grep and awk? Secondly using df -h is going to give you "human readable" output and is useless in the context you are trying to use it in namely doing numerical comparisons. df -k might make more sense, but a straight up df is probably what you really need. Since you are picking off the percentage of use that's the only way you are getting away with this. Try this:
        my $cmd=sprintf("df -k %s |",$FILESYSTEM) #note the trailing pipe in +the format open PIPE,$cmd or die "$cmd: $!"; my $junk=<PIPE>; # get rid of the first line.. it's junk. # # Could probably use /[\s]+/ but... my @df=split(/[\s\n\t]+/,<PIPE>) close PIPE; # done with this. my $df=$df[4]; #keep in mind the scalar $df is different than the arr +ay @df chomp $df; # just for grins $df =~ s/\%//g; # strip the % printf "File system %s is %d%% used\n",$FILESYSTEM,$df

        My next nit to pick:

        $DU = `du -sh $FILESYSTEM | sort -rn | head -20`;
        I'm not so sure that what you think this is doing is what is really happening. By doing a du -sh you are getting a summary of usage for that directory and not a list of the subdirectories with their usage. Using the -h flag also invalidates the numerical sorting you are trying to do. Here's my rewrite:
        open PIPE, sprintf("du -s %s/* |,$FILESYSTEM) or die $!; my @raw=<PIPE>; chomp @raw; my @summary=(); foreach my $line(@raw){ my($blocks,$subdir)=split(/[\s+]/,$line); push @summary,{ blocks=>$blocks, subdir=>$subdir }; } @summary=sort { $b->{blocks} <=> $a->{blocks} } @summary; printf "%s\n",join("\n", map { sprintf("%d\t%s",$_->{blocks},$_->{subdir}) } @summary[0..19] );
        Now I realize I've thrown some advanced stuff at ya, but I'll try and explain what's going on here. I've opened a pipe to collect the stdout of the command (I'll use /foo for a file system example)  du -s /foo/* and I've slurped it into an array @raw. I then iterate over that array and create an array of hashes (you'll hopefully see why in a second) where I've hashed the values for the subdirectory path and the number of blocks the subdirectory uses. (Actually it is files and subdirectories)

        Next thing I'm doing is sorting on the number of blocks in reverse. The bit that looks like { $b->{blocks} <=> $a->{blocks} } is an anonymous sub that governs how the sort function works. The normal behavior for sort would look like $a cmp $b which is to say an alphabetic forward sort rather than the numerical reverse sort we are doing.

        Here is where things get a bit snazzy and tricky at the same time. map { } @summary... iterates over the array passed to it. The bit about @summary[0..19] passes an array slice consisting of the first 20 elements in that array. The logic inside the map  map { sprintf("%d\t%s",$_->{blocks},$_->{subdir})} is putting the hash back together again and join("\n",... is taking the array coming out of map and making one big scalar (string) out of it.

        Having said that... just remember that in Perl TIMTOWTDI!


        Peter L. Berghold -- Unix Professional
        Peter -at- Berghold -dot- Net; AOL IM redcowdawg Yahoo IM: blue_cowdawg

        Have you considered writing your script in Bourne Shell instead of that... "perlshell" you are using?

Re: Getting error while executing the script
by toolic (Bishop) on Jun 13, 2012 at 15:28 UTC
    Read system and show your Perl code (diskusage.pl).
Re: Getting error while executing the script
by ww (Archbishop) on Jun 13, 2012 at 15:38 UTC
    Seeing your code (diskusage.pl) might be helpful: seeing the output of running the file in bash doesn't do much more than suggest what you might have in line 2 (but I'll bet its neither use strict nor use warnings).
      Hi Monk,

      Please find my complete script below

      #!/usr/local/bin/perl #Program to find the disk space and to delete the older files #Checks the type of OS here $OS = `uname -s`; print ("The Operating System is $OS"); if ($OS == SunOS ) { print ("Enter the filesystem related to SUN \n"); $FILESYSTEM = <STDIN>; $DF = `df -h $FILESYSTEM | grep -v "Filesystem" | sed -e "s/%//g"| +awk '{print $5}'`; print ("The file system $ FILESYSTEM usage is $DF % \n"); while ($DF > 50){ print ("The file system $FILESYSTEM reached its maximum capacity $ +DF % and request you to clear the space /n"); print (" Checking the disk usage of $FILESYSTEM and finding the to +p 20 culprits\n"); print ("###############################################"); $DU = `du -sh $FILESYSTEM | sort -rn | head -20`; print ("###############################################"); } else { ("The filesystem size looks good \n"); } }
        Attempting to execute (with Perl 5.014) your script , as you presented it, results in:
        syntax error ... near "} else" syntax error ... near "}" Execution of 976005.pl aborted due to compilation errors.

        The first complaint relates to the use of } else inside the while ($DF > 50){. The second grows out of the first.

        An if clause can use an else clause; while can't. You need to close the curly-brace of your while ($DF > 50){ before the } else (and that makes the brace at your line 38 redundant). You also need a verb such as print -- or say if you're using a recent Perl -- at your line 36. Consistently using indentation for if, when, while, unless (and so forth) would help make the missing } more obvious.

        Avoiding blank lines between each line of code might also be helpful (which is not to suggest removing all blank lines. They're useful visual cues (to some of us, anyway) to program flow... that one stanza or function is ending and another beginning.

        Including strict provides information about additional errors. But making those easily readable requires that you first use my with your variables, $OS, $FILESYSTEM and $DF so that the complaints that each $var "requires explicit package name at ..." don't swamp the most relevant (in thiscode) problem.

        When you've done so, you'll learn that SunOS is a bareword. If you put it in quotes and use the numeric comparison operator, ==, your script will still fail. Try eq instead.

        Finally, a general observation: while it's entirely possible and sometimes even useful to string a bunch of native OS commands together inside a Perl script, using `, system or </c>exec</c>, doing so can be the hard way to do things. You'd do well to explore Perl's own capabilities, and those found in modules in CPAN, such as "Filesys::Df"

        Update: See also df equivolent in Perl.

        Update 2: Several of the points here have been addressed by responders who were quicker to post than I but the fifth para (using strict directly after the italicized para) has now been updated re "readable" to explain the usage of "readable" which initially might have suggested that it's hard to read error message. That's not the point; the point was and is that finding the key error in a long string of error messages can require very careful attention.