0

I am building a git pre-commit hook to reject commits that contain string 'X'. I have a working version below.

#!/bin/sh

RED='\033[0;31m'
NC='\033[0m'

for FILE in `git diff --cached --name-only --diff-filter=ACM`; do
    if [ "grep 'X' $FILE" ]
    then
    echo -e "${RED}[REJECTED]${NC}"
    exit 1
fi
done
exit

What I would like to do is change the condition to look for string 'X', and if found, look for string 'Y' exactly 3 lines later. For example, X is on line 7 and Y is on line 10. I only want to reject commits with files containing strings 'X' and 'Y' separated by 3 lines. I have tried some funky things like:

if [ "grep -n 'X' $FILE" ] + 3 -eq [ "grep -n 'Y' $FILE" ]

How can I create the conditional I need? How best to generalise this?

Benjamin W.
  • 46,058
  • 19
  • 106
  • 116
  • 1
    The conditional is currently checking if the string `"grep 'X' $FILE"` is empty, which it never is, so the condition will always be true. You want something like `if grep -q 'X' "$FILE"` instead (no `[ ... ]` required). – Benjamin W. Jul 02 '20 at 15:59
  • And regarding the question about matching files with a pattern that spans multiple lines, see https://stackoverflow.com/q/2686147/3266847 – Benjamin W. Jul 02 '20 at 16:01
  • Hi Benjamin W., I believe you and you are likely right in theory. In practice, in this example, I've tested this logic with two files, one of which contains 'X' and the other which does not. The condition works as I expect. – user5513578 Jul 02 '20 at 22:23
  • Are you sure you're not using `[ \`grep 'X' $FILE\` ]`? I am absolutely positive that testing a non-empty string like `[ "grep 'X' $FILE" ]` has a successful exit status 100% of the time. – Benjamin W. Jul 02 '20 at 22:34

3 Answers3

1

I can pull it off, but there might be simpler ways:

for FILE in `git diff --cached --name-only --diff-filter=ACM`; do
    nl $FILE | grep 'X' - | while read line blah; do
        lines=$( head -n $(( $line + 3 )) $FILE | tail -n 1 | grep 'Y' | wc -l )
        if [ $lines -gt 0 ]; then
            echo you got it baby, on line $line for file $FILE
        fi
    done
done

Or something along those lines.

eftshift0
  • 26,375
  • 3
  • 36
  • 60
0

You can use grep -A to extract the three lines following X, and then check the last line for Y:

#!/bin/bash
file=$1
chunk=$(grep -A3 X "$file")
size=$(wc -l <<< "$chunk")
lastline=${chunk##*$'\n'}

if ((size == 4)) && grep -q Y <<< "$lastline" ; then
    echo Found both X and Y
fi

The size check is needed for the case when the Y follows X at the end of the file with less than two lines in between.

choroba
  • 231,213
  • 25
  • 204
  • 289
0

use sed to check for a pattern occurring n lines after your first pattern

sed -n '/<pattern1>/{n;n;n; /<patter2>/p}' $FILE
pyr0
  • 377
  • 1
  • 14