Better shell solution when blank lines may be piped to wc

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP











up vote
1
down vote

favorite












I have code that does something like this:



#!/bin/sh
CONTENTS=$(cat "somefile")
RELEVANT_LINES=$(echo "$CONTENTS" | grep -E "SEARCHEXPR")
COUNT=$(echo "$RELEVANT_LINES" | wc -l)


I hit an annoyance that this code didn't output the same if there weren't any matches, compared to the correct output given by replacing the third line with :



COUNT=$(echo "$CONTENTS" | grep -E "SEARCHEXPR" | wc -l)


I eventually traced to the fact that when there weren't any matches, RELEVANT_LINES was being set to the empty string, and echo was outputting a one line empty string + n for a line count of 1.



I tried using printf and echo -n in the 3rd line, but couldn't find the elegant workaround and ended up using COUNT=$(echo "$RELEVANT_LINES" | grep '0' | wc -l) (all lines contain a zero) to avoid having to regex filter the entire source file twice.



That can't be right, but I can't figure the correct fix.



I didn't drop to scripting with -eq '' because I wasn't sure it would be as robust and I prefer piping directly to wc for pure neatness.



Any hints how to get file content in a variable, to neatly distinguish zero vs one line after being filtered by grep? :)







share|improve this question
















  • 1




    Can't the whole thing be replaced by COUNT=$(grep -Ec "SEARCHEXPR" somefile) ?
    – steeldriver
    Jan 14 at 13:36














up vote
1
down vote

favorite












I have code that does something like this:



#!/bin/sh
CONTENTS=$(cat "somefile")
RELEVANT_LINES=$(echo "$CONTENTS" | grep -E "SEARCHEXPR")
COUNT=$(echo "$RELEVANT_LINES" | wc -l)


I hit an annoyance that this code didn't output the same if there weren't any matches, compared to the correct output given by replacing the third line with :



COUNT=$(echo "$CONTENTS" | grep -E "SEARCHEXPR" | wc -l)


I eventually traced to the fact that when there weren't any matches, RELEVANT_LINES was being set to the empty string, and echo was outputting a one line empty string + n for a line count of 1.



I tried using printf and echo -n in the 3rd line, but couldn't find the elegant workaround and ended up using COUNT=$(echo "$RELEVANT_LINES" | grep '0' | wc -l) (all lines contain a zero) to avoid having to regex filter the entire source file twice.



That can't be right, but I can't figure the correct fix.



I didn't drop to scripting with -eq '' because I wasn't sure it would be as robust and I prefer piping directly to wc for pure neatness.



Any hints how to get file content in a variable, to neatly distinguish zero vs one line after being filtered by grep? :)







share|improve this question
















  • 1




    Can't the whole thing be replaced by COUNT=$(grep -Ec "SEARCHEXPR" somefile) ?
    – steeldriver
    Jan 14 at 13:36












up vote
1
down vote

favorite









up vote
1
down vote

favorite











I have code that does something like this:



#!/bin/sh
CONTENTS=$(cat "somefile")
RELEVANT_LINES=$(echo "$CONTENTS" | grep -E "SEARCHEXPR")
COUNT=$(echo "$RELEVANT_LINES" | wc -l)


I hit an annoyance that this code didn't output the same if there weren't any matches, compared to the correct output given by replacing the third line with :



COUNT=$(echo "$CONTENTS" | grep -E "SEARCHEXPR" | wc -l)


I eventually traced to the fact that when there weren't any matches, RELEVANT_LINES was being set to the empty string, and echo was outputting a one line empty string + n for a line count of 1.



I tried using printf and echo -n in the 3rd line, but couldn't find the elegant workaround and ended up using COUNT=$(echo "$RELEVANT_LINES" | grep '0' | wc -l) (all lines contain a zero) to avoid having to regex filter the entire source file twice.



That can't be right, but I can't figure the correct fix.



I didn't drop to scripting with -eq '' because I wasn't sure it would be as robust and I prefer piping directly to wc for pure neatness.



Any hints how to get file content in a variable, to neatly distinguish zero vs one line after being filtered by grep? :)







share|improve this question












I have code that does something like this:



#!/bin/sh
CONTENTS=$(cat "somefile")
RELEVANT_LINES=$(echo "$CONTENTS" | grep -E "SEARCHEXPR")
COUNT=$(echo "$RELEVANT_LINES" | wc -l)


I hit an annoyance that this code didn't output the same if there weren't any matches, compared to the correct output given by replacing the third line with :



COUNT=$(echo "$CONTENTS" | grep -E "SEARCHEXPR" | wc -l)


I eventually traced to the fact that when there weren't any matches, RELEVANT_LINES was being set to the empty string, and echo was outputting a one line empty string + n for a line count of 1.



I tried using printf and echo -n in the 3rd line, but couldn't find the elegant workaround and ended up using COUNT=$(echo "$RELEVANT_LINES" | grep '0' | wc -l) (all lines contain a zero) to avoid having to regex filter the entire source file twice.



That can't be right, but I can't figure the correct fix.



I didn't drop to scripting with -eq '' because I wasn't sure it would be as robust and I prefer piping directly to wc for pure neatness.



Any hints how to get file content in a variable, to neatly distinguish zero vs one line after being filtered by grep? :)









share|improve this question











share|improve this question




share|improve this question










asked Jan 14 at 13:25









Stilez

436211




436211







  • 1




    Can't the whole thing be replaced by COUNT=$(grep -Ec "SEARCHEXPR" somefile) ?
    – steeldriver
    Jan 14 at 13:36












  • 1




    Can't the whole thing be replaced by COUNT=$(grep -Ec "SEARCHEXPR" somefile) ?
    – steeldriver
    Jan 14 at 13:36







1




1




Can't the whole thing be replaced by COUNT=$(grep -Ec "SEARCHEXPR" somefile) ?
– steeldriver
Jan 14 at 13:36




Can't the whole thing be replaced by COUNT=$(grep -Ec "SEARCHEXPR" somefile) ?
– steeldriver
Jan 14 at 13:36










1 Answer
1






active

oldest

votes

















up vote
4
down vote













Using CONTENTS to hold the file just to echo it again is a bit redundant, you could just



lines=$(cat "somefile" | grep -E "SEARCHEXPR")


or rather



lines=$(grep -E "SEARCHEXPR" "somefile")


If you just want the count of matching lines, use grep -c



count=$(grep -c -E "SEARCHEXPR" "somefile")


The immediate issue you see is caused by the fact that echo outputs the newline always. In the case where you get at least one line from the command substitutions, this actually helps, since command substitutions remove trailing newlines. It also works with tailing empty lines, try this to see: x=$(echo foo; echo; echo); echo "$x".



If you want to process the lines in the shell script in some other way, in addition to counting, then storing the text in a variable might not be best. You could try



for line in $lines ; do 
something with "$line"
done


But that has the usual issues with unquoted variables, i.e. filename globbing and word splitting on whitespace. One line containing "foo bar doo", would be seen as three, since by default, the spaces split too.



You might want to use a while read loop instead, but for that, a shell that supports process substitution might be good to have. See BashFAQ 001 and variable doesn't change after find & while and in bash, read after a pipe is not setting values .






share|improve this answer






















    Your Answer







    StackExchange.ready(function()
    var channelOptions =
    tags: "".split(" "),
    id: "106"
    ;
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function()
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled)
    StackExchange.using("snippets", function()
    createEditor();
    );

    else
    createEditor();

    );

    function createEditor()
    StackExchange.prepareEditor(
    heartbeatType: 'answer',
    convertImagesToLinks: false,
    noModals: false,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: null,
    bindNavPrevention: true,
    postfix: "",
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    );



    );








     

    draft saved


    draft discarded


















    StackExchange.ready(
    function ()
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f417023%2fbetter-shell-solution-when-blank-lines-may-be-piped-to-wc%23new-answer', 'question_page');

    );

    Post as a guest






























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes








    up vote
    4
    down vote













    Using CONTENTS to hold the file just to echo it again is a bit redundant, you could just



    lines=$(cat "somefile" | grep -E "SEARCHEXPR")


    or rather



    lines=$(grep -E "SEARCHEXPR" "somefile")


    If you just want the count of matching lines, use grep -c



    count=$(grep -c -E "SEARCHEXPR" "somefile")


    The immediate issue you see is caused by the fact that echo outputs the newline always. In the case where you get at least one line from the command substitutions, this actually helps, since command substitutions remove trailing newlines. It also works with tailing empty lines, try this to see: x=$(echo foo; echo; echo); echo "$x".



    If you want to process the lines in the shell script in some other way, in addition to counting, then storing the text in a variable might not be best. You could try



    for line in $lines ; do 
    something with "$line"
    done


    But that has the usual issues with unquoted variables, i.e. filename globbing and word splitting on whitespace. One line containing "foo bar doo", would be seen as three, since by default, the spaces split too.



    You might want to use a while read loop instead, but for that, a shell that supports process substitution might be good to have. See BashFAQ 001 and variable doesn't change after find & while and in bash, read after a pipe is not setting values .






    share|improve this answer


























      up vote
      4
      down vote













      Using CONTENTS to hold the file just to echo it again is a bit redundant, you could just



      lines=$(cat "somefile" | grep -E "SEARCHEXPR")


      or rather



      lines=$(grep -E "SEARCHEXPR" "somefile")


      If you just want the count of matching lines, use grep -c



      count=$(grep -c -E "SEARCHEXPR" "somefile")


      The immediate issue you see is caused by the fact that echo outputs the newline always. In the case where you get at least one line from the command substitutions, this actually helps, since command substitutions remove trailing newlines. It also works with tailing empty lines, try this to see: x=$(echo foo; echo; echo); echo "$x".



      If you want to process the lines in the shell script in some other way, in addition to counting, then storing the text in a variable might not be best. You could try



      for line in $lines ; do 
      something with "$line"
      done


      But that has the usual issues with unquoted variables, i.e. filename globbing and word splitting on whitespace. One line containing "foo bar doo", would be seen as three, since by default, the spaces split too.



      You might want to use a while read loop instead, but for that, a shell that supports process substitution might be good to have. See BashFAQ 001 and variable doesn't change after find & while and in bash, read after a pipe is not setting values .






      share|improve this answer
























        up vote
        4
        down vote










        up vote
        4
        down vote









        Using CONTENTS to hold the file just to echo it again is a bit redundant, you could just



        lines=$(cat "somefile" | grep -E "SEARCHEXPR")


        or rather



        lines=$(grep -E "SEARCHEXPR" "somefile")


        If you just want the count of matching lines, use grep -c



        count=$(grep -c -E "SEARCHEXPR" "somefile")


        The immediate issue you see is caused by the fact that echo outputs the newline always. In the case where you get at least one line from the command substitutions, this actually helps, since command substitutions remove trailing newlines. It also works with tailing empty lines, try this to see: x=$(echo foo; echo; echo); echo "$x".



        If you want to process the lines in the shell script in some other way, in addition to counting, then storing the text in a variable might not be best. You could try



        for line in $lines ; do 
        something with "$line"
        done


        But that has the usual issues with unquoted variables, i.e. filename globbing and word splitting on whitespace. One line containing "foo bar doo", would be seen as three, since by default, the spaces split too.



        You might want to use a while read loop instead, but for that, a shell that supports process substitution might be good to have. See BashFAQ 001 and variable doesn't change after find & while and in bash, read after a pipe is not setting values .






        share|improve this answer














        Using CONTENTS to hold the file just to echo it again is a bit redundant, you could just



        lines=$(cat "somefile" | grep -E "SEARCHEXPR")


        or rather



        lines=$(grep -E "SEARCHEXPR" "somefile")


        If you just want the count of matching lines, use grep -c



        count=$(grep -c -E "SEARCHEXPR" "somefile")


        The immediate issue you see is caused by the fact that echo outputs the newline always. In the case where you get at least one line from the command substitutions, this actually helps, since command substitutions remove trailing newlines. It also works with tailing empty lines, try this to see: x=$(echo foo; echo; echo); echo "$x".



        If you want to process the lines in the shell script in some other way, in addition to counting, then storing the text in a variable might not be best. You could try



        for line in $lines ; do 
        something with "$line"
        done


        But that has the usual issues with unquoted variables, i.e. filename globbing and word splitting on whitespace. One line containing "foo bar doo", would be seen as three, since by default, the spaces split too.



        You might want to use a while read loop instead, but for that, a shell that supports process substitution might be good to have. See BashFAQ 001 and variable doesn't change after find & while and in bash, read after a pipe is not setting values .







        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Jan 14 at 13:46

























        answered Jan 14 at 13:40









        ilkkachu

        49.8k674137




        49.8k674137






















             

            draft saved


            draft discarded


























             


            draft saved


            draft discarded














            StackExchange.ready(
            function ()
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f417023%2fbetter-shell-solution-when-blank-lines-may-be-piped-to-wc%23new-answer', 'question_page');

            );

            Post as a guest













































































            Popular posts from this blog

            How to check contact read email or not when send email to Individual?

            Displaying single band from multi-band raster using QGIS

            How many registers does an x86_64 CPU actually have?