2

I do have the following find-file script:

find . -type f -iname "*$1*" -printf '%P\n' \
| GREP_COLORS="sl=0;33;49:ms=1;34;49" grep --color=always '^\|[^/]*$' \
| grep -i --color=auto "$1"

which does:

  • colorize the path part
  • colorize the pattern in the base name part

enter image description here

Problem: after the pattern, the colour is reset

For the base name part, it could be entirely white, that'd solve the problem (though I did not find the way to change this).

For the path part, it'd stay a problem, as you can see in the above screenshot: the colour is reset, hence we don't see anymore where the file path really ends!

Is there a solution to this?

EDIT -- There were 3 solutions to this!! All below proposals do work, they have light differences, but they do answer the question. I choose the one which highlights all occurrences of the pattern, and is almost a one-line, but honestly choosing was tough, as they're quite equivalent…

EDIT -- One wished improvement to this: that the find results don't get blocked until analysis, that is that the output gets flushed and processed line per line. Is this possible?

1
  • Someone (who already did delete his comment) added to use the option ne of GREP_COLORS (see man grep). Tried it, but adding GREP_COLORS="ne=1" before the last grep does not solve the problem. It even uses the GREP_COLOR instead!? Commented Jan 21, 2020 at 8:49

3 Answers 3

2

maybe something like this?

enter image description here

command:

pattern='oRg'; find . -type f -iname "*$pattern*" -printf '%P\n' \
| GREP_COLORS="sl=0;33:mt=1;34" grep --color=always '[^/]*$' \
| GREP_COLORS="sl=1;34" grep --color=always -iP "$pattern(?=[^/]*$)" \
| GREP_COLORS="sl=0;33" grep -i "$pattern" --color

remove the last line if you don't want highlight the pattern in dirname part.

see grep's Environment Variable GREP_COLORS section for details.

4
  • What about the "non-blocking" display of find's results, as soon as they're found? Commented Jan 23, 2020 at 17:44
  • Thanks for trying. Well, I have the impression is still waits a long time, and then processes every line at a constant pace. But, anyway, that last solution is much longer: searching for README from my root dir takes 36 seconds with the previous version, and 2 min 17 sec with the new one... Commented Jan 23, 2020 at 19:02
  • Hi, I didn't understand what you mean is about non-blocking find output. if you mean you want tonsee the result as soon as a file found, well the comand is exactly what you want but maybe it's too fast you see output all appear at once. you can add ... |more to the command so you can review page by page. if I misunderstood your requirement please do edit your question and clarify what you mean. thanks Commented Jan 24, 2020 at 6:47
  • Hi, I mean: forcing flushing of findoutput after every line, instead of keeping the output buffered up to the end of the find command. Is this clearer now? (OK for the more, that's not about it though.) Commented Jan 24, 2020 at 8:14
1

You could do it with zsh's builtin glob operators. That would have several benefits:

  • fix this issue easily
  • work with pathnames that contain newline characters
  • make it easy to highlight the pattern in the basename only
  • work with wildcards (in your approach, find and grep interpret the pattern differently)
  • get you a sorted list
  • work even on non-GNU systems (-printf, -iname, --color are all non-standard extensions).

Maybe something like:

#! /bin/zsh -
pattern="(#i)${1?Please specify a pattern}"

set -o extendedglob

typeset -A find_file_color
find_file_color=(
  dirname  $'\e[0;33;49m'
  basename $'\e[1;34;49m'
  match    $'\e[1;33;44m'
  reset    $'\e[m'
)

colorize_file() {
  local file=${1-$REPLY}
  case $file in
    (*/*)
      REPLY=$find_file_color[dirname]$file:h$find_file_color[reset]/;;
    (*)
      REPLY=
  esac
  REPLY+=$find_file_color[basename]${${file:t}//(#m)$~pattern/$find_file_color[match]$MATCH$find_file_color[basename]}$find_file_color[reset]
}

print -rC1 -- **/*$~pattern*(ND.+colorize_file)

Note that it builds and sorts the whole list before passing to print for printing. So you'll only start getting some output once all the files have been found. To print them as they are found (but then we'd need to give up on sorting), you could have the glob qualifier function print the colorized file instead:

#! /bin/zsh -
pattern="(#i)${1?Please specify a pattern}"

set -o extendedglob

typeset -A find_file_color
find_file_color=(
  dirname  $'\e[0;33;49m'
  basename $'\e[1;34;49m'
  match    $'\e[1;33;44m'
  reset    $'\e[m'
)

colorize_file() {
  local file=${1-$REPLY}
  case $file in
    (*/*)
      REPLY=$find_file_color[dirname]$file:h$find_file_color[reset]/;;
    (*)
      REPLY=
  esac
  REPLY+=$find_file_color[basename]${${file:t}//(#m)$~pattern/$find_file_color[match]$MATCH$find_file_color[basename]}$find_file_color[reset]
  print -r -- $REPLY
  false # don't bother adding the file to the glob expansion
}

: **/*$~pattern*(ND.+colorize_file)
5
  • Thanks @Stéphane, this does work fine. Just a glitch: pattern is not highlighted "insensitively": only in the same case. Commented Jan 23, 2020 at 12:27
  • this answer looks good, except the tiny point above, but it's a bit too complex for me, and I'm looking for something that could run in other shells than Zsh, in order to run on my Synology NAS, on a Raspberry, etc. So looking for something more portable… Commented Jan 23, 2020 at 12:45
  • Sorry, my bad I had assumed nocasematch applied to wildcards, but it applies to regexps instead. I've fixed it by adding (#i) to the pattern instead. You can always use find-file '(#I)org' to match case sensitively. I've made it a standalone script as well, so you can call it from other shells. Note that zsh is 30 year old, almost as old as bash, older than Linux anyways and ported to most systems. I'd expect you'll find a synology package for it. Commented Jan 23, 2020 at 15:11
  • Thanks for the script, and the extensive comments! Quick question: what is the role of the - after zsh, in the shebang? Commented Jan 24, 2020 at 8:50
  • @user3341592, see Why the "-" in the "#! /bin/sh -" shebang? Commented Jan 24, 2020 at 9:48
0

1) a example how to colorize text

# creation of 3 escape sequences
C_1=$(echo -ne "\033[30;47m" ) ;
C_2=$(echo -ne "\033[32;41m" ) ;
C_3=$(echo -ne "\033[37;44m" ) ;
C_N=$(echo -ne "\033[0m"  )  ;

#simple example for using it
echo $C_1 Hello in C_1 $C_N normal ;
echo $C_2 Hello in C_2 $C_N normal ;
echo $C_3 Hello in C_3 $C_N normal ;

Explanation of values are here : https://en.wikipedia.org/wiki/ANSI_escape_code#3/4_bit

In your case because you are looking for simple pattern i can use only one sed command .

PAT="tot" ; 
find ./ -iname  \*$PAT\*  -printf '%P\n' | \
    sed "s/^\(.*\/\)\{0,1\}\([^\/]*\)\($PAT\)\([^\/]*\)\$/${C_1}\1${C_2}\2${C_3}\3${C_N}\4/i"

\1 is the full path ending with the last /
\2 is the pattern of the filename before the pattern
\3 is the pattern
\4 is after the pattern
4
  • This one-liner looks nice… However, it does not highlight the pattern for files located at the root directory of the search. Commented Jan 21, 2020 at 9:04
  • i modified my response Commented Jan 21, 2020 at 16:12
  • When testing it, I've got: sed: -e expression #1, char 84: Unmatched \{ Commented Jan 23, 2020 at 12:30
  • i fixed my typo. Commented Jan 23, 2020 at 17:10

You must log in to answer this question.

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