3

I want to have a bash script which:

  1. Runs the "strings" command on each file in current directory
  2. Searches the output of strings for each file for specific terms using grep

I have the following, but the script output does not show any matches:

#!/bin/bash

echo "Searching files in directory for secrets and urls"

for file in ./*
do
   echo "=====$file====="
   strings ${file} | egrep -wi --color 'secret\|password\|key\|credential|\http'
done

I've also tried strings $file | egrep -wi --color 'secret\|password\|key\|credential|\http' and eval "strings ${file} | egrep -wi --color 'secret\|password\|key\|credential|\http'" but these do not appear to work. The script outputs the filenames, but not the matches.

2 Answers2

11

You're using egrep which is the same as grep -E, i.e. it enables the use of extended regular expressions.

In an extended regular expression, | is an alternation (which is what you want to use), and \| matches a literal | character.

You therefore want

grep -w -i -E 'secret|password|key|credential|http'

or

grep -i -E '\<(secret|password|key|credential|http)\>'

where \< and \> matches word boundaries.

Or

grep -w -i -F \
    -e secret      \
    -e password    \
    -e key         \
    -e credential  \
    -e http

... if you want to do string comparisons rather than regular expression matches.

Additionally, you will want to always double quote variable expansions. This would allow you to also process files with names that contain whitespace characters (space, tab, newline) and names that contain filename globbing characters (*, ?, [...]) correctly:

#!/bin/sh

for name in ./*; do
    [ ! -f "$name" ] && continue    # skip non-regular files

    printf '==== %s ====\n' "$name"
    strings "$name" | grep ...
done

See also

Kusalananda
  • 333,661
  • The strings util defaults to strings of 4 or more chars, so it would fail to send grep the lone string "key", (but it would send 6 chars "keymap"). – agc May 02 '20 at 21:11
  • @agc Yes, or key: or whatever other string that key may be a substring of. This is not an issue with the answer, but with the original approach. – Kusalananda May 02 '20 at 21:37
2

A for loop is unnecessary. Use strings to output a filename and a decimal offset, then pipe any strings of at least three chars long to egrep:

strings -n 3 -f -t d ./* 2> /dev/null | 
egrep '[[:alnum:][:punct:]]*(secret|password|key|credential|http)'\
'[[:alnum:][:punct:]]*$' 

Three chars instead of the default four is needed so as not to miss "key".

Since we lack sample input files, here's a demo showing the first ten hits in the /bin/ directory:

strings -n 3 -f -t d /bin/* 2> /dev/null |  
egrep '[[:alnum:][:punct:]]*(secret|password|key|credential|http)'\
'[[:alnum:][:punct:]]*$' | 
head

Output on my system:

/bin/bash:   78590 rl_discard_keymap
/bin/bash:   78720 rl_executing_key
/bin/bash:   79076 rl_bind_key
/bin/bash:   79847 emacs_standard_keymap
/bin/bash:   79905 _rl_keymap
/bin/bash:   81110 _rl_executing_keyseq_size
/bin/bash:   81598 rl_bind_keyseq_if_unbound
/bin/bash:   81640 rl_bind_keyseq
/bin/bash:   81736 bind_keyseq_to_unix_command
/bin/bash:   81863 _rl_dispatching_keymap
agc
  • 7,223