Advertisement
aikikode

Multiline 'grep' tool

Dec 3rd, 2011
143
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Bash 6.61 KB | None | 0 0
  1. #!/bin/bash -
  2. #===============================================================================
  3. #
  4. #          FILE:  mlgrep.sh
  5. #
  6. #         USAGE:  ./mlgrep.sh [OPTION]... PATTERN [FILE]...
  7. #
  8. #   DESCRIPTION:  Script provides the ability to search multiline regexp in the
  9. #                 file. The algo is simple:
  10. #                 1) store the pattern to search in const buffer
  11. #                 2) read input file line by line into circular buffer
  12. #                 3) as soon as the input buffer is full perform match:
  13. #                    a) if match found - print & empty input buffer
  14. #                    b) if not - print (in case of invert match) the first line
  15. #                                from the buffer
  16. #                 4) when EOF is reached, print (in case of invert match) the
  17. #                    rest of the buffer
  18. #
  19. #       OPTIONS:  [-n]             - print line numbers
  20. #                 [-v]             - invert match
  21. #                 [-i]             - ignore case
  22. #                 [-f]             - read pattern from file
  23. #                 <pattern (file)> - pattern (or the file with pattern) to
  24. #                                    search, it should have one or several
  25. #                                    lines in form of regular expressions
  26. #                 <input file>     - input text file or stdin
  27. #===============================================================================
  28.  
  29. set -o nounset                              # Treat unset variables as an error
  30.  
  31. PRINT_NUM=0
  32. INVERT=0
  33. IGNORE_CASE=0
  34. FROM_FILE=0
  35. torem=0
  36.  
  37.  
  38.  
  39. function usage()
  40. {
  41.     echo -e "Usage: $(basename $0) [OPTION]... PATTERN [FILE]...\nTry \`$(basename $0) -h\` for more information."
  42.     exit 1
  43. }
  44.  
  45. while getopts "nvifh" opt
  46. do
  47.     case "$opt" in
  48.         n) PRINT_NUM=1;;
  49.         v) INVERT=1;;
  50.         i) IGNORE_CASE=1;;
  51.         f) FROM_FILE=1;;
  52.         h)
  53.             cat << EOF
  54. $(basename $0) is 'Multiline grep' tool.
  55.  
  56. Usage: $(basename $0) [OPTION]... PATTERN [FILE]...
  57. Search for PATTERN in each FILE or standard input.
  58. PATTERN is awk-style regular expression ('\n' are
  59. allowed to represent multiline regexp)
  60.  
  61. OPTIONS:
  62.   -n       - print line numbers
  63.   -v       - invert match
  64.   -i       - ignore case
  65.   -f       - read pattern from file
  66. EOF
  67. exit 0;;
  68.     esac
  69. done
  70. shift $(($OPTIND - 1))
  71.  
  72. if [ $# -eq 0 ]
  73. then
  74.     usage
  75. fi
  76.  
  77. if [[ $FROM_FILE -eq 1 && -s "$1" ]]
  78. then
  79.     PATTERN_FILE="$1"
  80. else
  81.     PATTERN_FILE=$(mktemp)
  82.     torem=1
  83.     echo -e $1 > $PATTERN_FILE
  84. fi
  85. shift
  86.  
  87. awk -vprint_num=$PRINT_NUM -vinvert=$INVERT -vignore_case=$IGNORE_CASE -vpattern_file=$PATTERN_FILE 'BEGIN {
  88.    # save pattern to search for from file to const buffer
  89.    patternlen = 0;
  90.    while (getline < pattern_file)
  91.    {
  92.        if (ignore_case)
  93.            pattern[patternlen] = tolower($0);
  94.        else
  95.            pattern[patternlen] = $0;
  96.  
  97.        patternlen++;
  98.    }
  99.  
  100.    # user circular buffer
  101.    head = 0;
  102.    tail = 0;
  103.    empty = 1;
  104. }
  105.  
  106. # Increment user circular buffer index
  107. function increment(count)
  108. {
  109.    if (count == (patternlen - 1))
  110.        count = 0;
  111.    else
  112.        count++;
  113.  
  114.    return count;
  115. }
  116.  
  117. # Whether user buffer is full
  118. function isfull()
  119. {
  120.    if (increment(tail) == head)
  121.        return 1;
  122.    else
  123.        return 0;
  124. }
  125.  
  126. {
  127.    # Add current string to circular buffer and move indexes if necessary
  128.    if (!empty)
  129.    {
  130.        if (isfull())
  131.            head = increment(head);
  132.  
  133.        tail = increment(tail);
  134.    }
  135.    buf[tail] = $0;
  136.    empty = 0;
  137.  
  138.    # Check current buffer against the saved pattern;
  139.    # check makes sense only if buffer is full
  140.    if (isfull())
  141.    {
  142.        equal = 1;
  143.        j = head;
  144.        for (i = 0; i < patternlen; i++)
  145.        {
  146.            if (ignore_case)
  147.            {
  148.                if (!match(tolower(buf[j]),pattern[i]))
  149.                {
  150.                    equal = 0;
  151.                    break;
  152.                }
  153.            }
  154.            else
  155.            {
  156.                if (!match(buf[j],pattern[i]))
  157.                {
  158.                    equal = 0;
  159.                    break;
  160.                }
  161.            }
  162.  
  163.  
  164.            j = increment(j);
  165.        }
  166.  
  167.        # Behaviour depends on what is needed: print only found or print all
  168.        # but found
  169.        if (equal)
  170.        {
  171.            if (!invert)
  172.            {
  173.                j = head;
  174.                if (print_num)
  175.                {
  176.                    for (i = 0; i < patternlen; i++)
  177.                    {
  178.                        printf "%d:%s\n", NR - patternlen + i + 1, buf[j];
  179.                        j = increment(j);
  180.                    }
  181.                }
  182.                else
  183.                {
  184.                    for (i = 0; i < patternlen; i++)
  185.                    {
  186.                        printf "%s\n", buf[j];
  187.                        j = increment(j);
  188.                    }
  189.                }
  190.  
  191.            }
  192.            empty = 1;
  193.            head = tail;
  194.        }
  195.        else if (invert)
  196.        {
  197.            if (print_num)
  198.                printf "%d:%s\n", NR - patternlen + 1, buf[head];
  199.            else
  200.                print buf[head];
  201.        }
  202.    }
  203. }
  204. END {
  205.    # Need to print out what is left in the user buffer.
  206.    # No need to print anything if the pattern has just been found
  207.    if (!empty && invert)
  208.    {
  209.        if (!isfull())
  210.        {
  211.            if (print_num)
  212.            {
  213.                buflen = 0;
  214.                for (j = head; increment(tail) != j; j = increment(j))
  215.                    buflen++;
  216.  
  217.                i = 0;
  218.                for (j = head; increment(tail) != j; j = increment(j))
  219.                {
  220.                    printf "%d:%s\n", NR - buflen + i + 1, buf[j];
  221.                    i++;
  222.                }
  223.            }
  224.            else
  225.            {
  226.                for (i = head; ; i = increment(i))
  227.                {
  228.                    print buf[i];
  229.                    if (i == tail)
  230.                        break;
  231.                }
  232.            }
  233.        }
  234.        else
  235.        {
  236.            if (print_num)
  237.            {
  238.                i = 1;
  239.                for (j = increment(head); j != head; j = increment(j))
  240.                {
  241.                    printf "%d:%s\n", NR - patternlen + i + 1, buf[j];
  242.                    i++;
  243.                }
  244.            }
  245.            else
  246.            {
  247.                for (j = increment(head); j != head; j = increment(j))
  248.                {
  249.                    print buf[j];
  250.                }
  251.            }
  252.        }
  253.    }
  254. }' $*
  255.  
  256. if [ $torem -eq 1 ]
  257. then
  258.     rm $PATTERN_FILE
  259. fi
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement