21

I have following output from git status, how do I grep for everything after Untracked files:

[alexus@wcmisdlin02 Test]$ git status 
# On branch master
#
# Initial commit
#
# Changes to be committed:
#   (use "git rm --cached <file>..." to unstage)
#
#   new file:   app/.gitignore
#   new file:   app/app.iml
#   new file:   app/build.gradle
#   new file:   app/proguard-rules.pro
#   new file:   app/src/androidTest/java/org/alexus/test/ApplicationTest.java
#   new file:   app/src/main/AndroidManifest.xml
#   new file:   app/src/main/java/org/alexus/test/MainActivity.java
#   new file:   app/src/main/res/layout/activity_main.xml
#   new file:   app/src/main/res/menu/menu_main.xml
#   new file:   app/src/main/res/mipmap-hdpi/ic_launcher.png
#   new file:   app/src/main/res/mipmap-mdpi/ic_launcher.png
#   new file:   app/src/main/res/mipmap-xhdpi/ic_launcher.png
#   new file:   app/src/main/res/mipmap-xxhdpi/ic_launcher.png
#   new file:   app/src/main/res/values-w820dp/dimens.xml
#   new file:   app/src/main/res/values/dimens.xml
#   new file:   app/src/main/res/values/strings.xml
#   new file:   app/src/main/res/values/styles.xml
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#   .gitignore
#   .idea/
#   Test.iml
#   build.gradle
#   gradle.properties
#   gradle/
#   gradlew
#   gradlew.bat
#   settings.gradle
[alexus@wcmisdlin02 Test]$ 

Like this, but without specifying number of lines, like the -A parameter in GNU grep:

[alexus@wcmisdlin02 Test]$ git status | grep -A100 'Untracked files'
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#   .gitignore
#   .idea/
#   Test.iml
#   build.gradle
#   gradle.properties
#   gradle/
#   gradlew
#   gradlew.bat
#   settings.gradle
[alexus@wcmisdlin02 Test]$ 

Is there a way to do it?

[alexus@wcmisdlin02 Test]$ grep --version
grep (GNU grep) 2.20
Copyright (C) 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Written by Mike Haertel and others, see <http://git.sv.gnu.org/cgit/grep.git/tree/AUTHORS>.
[alexus@wcmisdlin02 Test]$ 
alexus
  • 13,112
  • 32
  • 117
  • 174
  • Why can't you use `-A` with a large enough number as argument? – kasperd May 29 '15 at 17:17
  • For this example however, you should not "grep after", it would be easier & less error prone to use git's "_porcelain_" mode: `git status --porcelain | grep '^??' | cut -f2 -d' '` – conny Aug 29 '21 at 20:32

6 Answers6

24

With GNU grep (tested with version 2.6.3):

git status | grep -Pzo '.*Untracked files(.*\n)*'

Uses -P for perl regular expressions, -z to also match newline with \n and -o to only print what matches the pattern.

The regex explained:

First we match any character (.) zero or multiple times (*) until an occurence of the string Untracked files. Now, the part inside the brackets (.*\n) matches any character except a newline (.) zero or multiple times (*) followed by a newline (\n). And all that (that's inside the backets) can occure zero or multiple times; that's the meaning of the last *. It should now match all other lines, after the first occurence of Untracked files.

chaos
  • 1,505
  • 1
  • 15
  • 21
  • 3
    An answer that actually uses `grep`, I'm impressed! – Hyppy May 29 '15 at 16:35
  • @hyppy me too) and then again, match also evolved Perl regex) – alexus May 29 '15 at 16:38
  • @chaos can you explain regex please? I don't really understand last `*`. – alexus May 29 '15 at 17:09
  • @alexus I edit the answer with an explanation – chaos May 29 '15 at 17:48
  • 1
    This doesn't work well if the input has a lot of lines. I tested it on Ubuntu 14.04, and if the match is longer than about 8000 lines, it segfaults. – kasperd May 29 '15 at 17:49
  • 2
    @kasperd I think with 8000+ lines, `grep` isn't the right tool. But otherwise this question is (i think) for the user to see only the relevant output, what you obviously don't have at 8000+ lines, `grep`ed or not. – chaos May 29 '15 at 17:53
  • 1
    @chaos I also tested `grep -A999999999 'Untracked files'` with 1000000 lines before the match and 1000000 lines after the match. That approach worked just fine. So `grep` can do it. The problem with your approach is that you are forcing it to perform a single match on the entire file rather than on individual lines. – kasperd May 29 '15 at 17:58
  • @kasperd True, good point, `-A999999999` Just matches a pattern an then lets 99999999 lines trough, without processing them, my approach, on the other side, tries to match everything to the end, which forces grep to process every line. – chaos May 29 '15 at 18:00
  • this won't work properly if the file actually contains null bytes though ;) – hanshenrik Mar 21 '22 at 16:47
18

If you don't mind using sed, here is a possible solution

git status | sed -n -e '/Untracked files/,$p'
rahul
  • 281
  • 3
  • 6
8

I would use awk for this:

git status | awk '/Untracked files/,0'

This /Untracked files/,0 is a range exression. It evaluates to True from the first time Untracked files and until 0 evaluates to True. Since this never happens, it prints the rest of the file.

Note that awk's behaviour to a True is to print the current record (line, normally), that's why we don't have to explicitly call print.

fedorqui
  • 258
  • 4
  • 17
3

You may try:

git status | grep -A99 Untracked

where -A would print lines of trailing context after the match.

Alternatively use more or less, for example:

git status | less +/Untracked

For scripting purposes, I'd use ex/vi:

git status | ex -s +"/Untracked/norm d1G" +%p -cq! /dev/stdin

To include line with Untracked files, use kd1G instead of d1G.

Or without piping and /dev/stdin, the syntax would be: ex ... <(git status).

kenorb
  • 6,499
  • 2
  • 46
  • 54
1

For a one-liner with parenthesis to spawn a subshell so you avoid retaining the variables:

(while read LINE ; do [[ "$LINE" =~ "Untracked files" ]] && MATCHED=1 ; \
   [[ "$MATCHED" = 1 ]] && echo "$LINE" ; done < <(git status))

You can just pipe at the end of that into whatever else you need to do with it.

Or you can use it in whatever shell script

while read LINE ; do
    [[ "$LINE" =~ "Untracked files" ]] && MATCHED=1
    [[ "$MATCHED" = 1 ]] && echo "$LINE"
done < <(git status)

Replace the echo "$LINE" with whatever you want to do with the data. If you want to skip the "Untracked files" line, then just switch the two tests.

Hyppy
  • 15,608
  • 1
  • 38
  • 59
0

you can use something like this, I used Perl, the following command set count = 1 if I match some string in the current line and when the while loop is finished I check the count variables and I print the range of lines from where match happen to the last line

ls -al | perl -e '@LINES = (); $n = 0 ; $nline = 0; while(<>){ push(@LINES, $_); $n++; if($_ =~ m/mirror/i) { $count = 1; $nline = $. - 1 };};  if($count != 0){ foreach $i($nline..$n-1){ print "\t$LINES[$i]\n";}}'
c4f4t0r
  • 5,301
  • 3
  • 31
  • 42
  • 1
    Much too complicated. A simple Perl solution is here: http://stackoverflow.com/a/18167194/111036. A variation which includes the line to match could be: `perl -ne 'print if /Untracked files:/ .. 0'` – mivk Jul 23 '16 at 16:47