I am trying to find all *.md at ${PWD}, then grep for lines beginning with title: and print it along with the absolute filepath -H and the line number of the pattern match -n, then I try to output the result in a table with separator -s as :

find ${PWD} -type f -name "*.md" -exec bash -c 'i="$1"; grep -HnE "^title:" $1; | column -t -s :;' _ {} \;

This is working till grep but I am unable to create a table.

Also, from the correction suggested by @steeldriver, the exec seems to be happening on each find result one by one, thereby table is not being created?

Sample Output

/Admin/Specification/Specification.md    2  title   Specification
/Admin/GraphicCard/index.md              2  title   "GraphicCard"
  • What's the purpose in life of that underscore before {}? And what actually happens? Sample output would go a long way...
    – tink
    Jan 10, 2019 at 17:03
  • _ is for $0
    – Porcupine
    Jan 10, 2019 at 17:03
  • 2
    Try removing the ; between the grep command and the pipe ... Jan 10, 2019 at 17:06
  • @steeldriver Thanks! If I redirect the results as find ${PWD} -type f -name "*.md" -exec bash -c 'i="$1"; grep -HnE "^title:" $1 | column -t -s : ' _ {}\; > FILE The columns are not preserved anymore. Could you suggest why?
    – Porcupine
    Jan 10, 2019 at 17:13
  • 1
    If your find supports it, you can use {} + in place of {} \; Jan 10, 2019 at 17:25

1 Answer 1


You might be overthinking things. Rather than trying to tabulate the results of grep, why not let awk, a prolific table-maker, make the table for you?

find . -type f -name \*.md -print0 | xargs -0 awk 'BEGIN { OFS="\t" } /^title:/ { print FILENAME, FNR, $0 }'

find will locate all the md files you're interested in, and pass them all as arguments to awk by way of xargs. We then just have awk spit out, on lines that match your criterium, the name of the file (which find will have passed with the full relative path), the record number in the file (i. e. the line number), and the line as seen in the file; all separated by tabs as defined in OFS, the Output Field Separator.

If you want to be a little more exact with the alignment of your output, you could instead do something like:

/^title:/ { printf( "%35s %4d %s\n", FILENAME, FNR, $0 ) }

Also, if you don't want to find | xargs awk but would rather find -exec, this also works:

find . -name \*.md -type f -exec awk '/expiry/ { printf( "%30s %4d %s\n", FILENAME, FNR, $0 ) }' "{}" +
  • How about -exec with +;? Is it better if I am not concerned with portability?
    – Porcupine
    Jan 10, 2019 at 18:23
  • That will also work just fine, yes. I have added that invocation to the answer.
    – DopeGhoti
    Jan 10, 2019 at 18:26

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .