The question wants me to list all the files stored in /usr/bin which the file names only contain lowercase English letters and contain the word 'file'.

For example, nsrfile and file should be in the output

Here's my code to achieve this

ls /usr/bin | grep '\<[a-z]*file[a-z]*\>'

and here's some part of the result I get.


however, the question says that the file name should only contain lowercase English letters but not the slash -

and grub2-file even contains a number

how should I change my commands?


Expectation and warning

Your assignment may expect you to grep the output of ls, perhaps something like this:

cd /usr/bin
ls | grep '^[a-z]*$' | xargs grep file

Where you ask ls for the filenames, then have grep filter ones that start (^) and end ($) with only lower-case letters -- zero or more of them (*); you then ask xargs to grep each incoming filename for the string "file".

This runs into immediate trouble as soon as any filenames (unlikely as these are to be in /usr/bin) contain a newline. (Files with spaces in them Some File Name will be excluded by the grep.) For a contrived example to show the point:

cd ~/tmp/usr/bin
touch a$'\n'b
ls | grep '^[a-z]*$' | xargs grep file
grep: a: No such file or directory
grep: b: No such file or directory

In the above example, ls wrote the following contents to the pipe for grep:


Grep rightfully passed both of those "filenames" down the pipe to xargs, which then thought it had two filenames to grep against, and so it ran:

grep file a b

... which then complained about the missing files.


While it's not expectedd, I would suggest the following safer option, assuming a bash shell:

shopt -s extglob
grep -l file /usr/bin/!(*[^a-z]*)

This turns on bash's extended globbing functionality. You then ask grep to list the filenames (-l) that contain the string "file" among the files in /usr/bin that do not (!) match the pattern: "anything (*) followed by any single ([ ... ]) non (^) -lower-case letter (a-z), followed by anything (*). In other words, only files that contain only lower-case letters in their name.

| improve this answer | |
  • Thanks so much for those details. xargs would be the best thing I've learnt today – Ulysses Jan 31 '18 at 20:02

What I would do :

printf '%s\n' /usr/bin/* |
    grep '\<[a-z]*file[a-z]*\>' | grep -Ev '[0-9]|-'

Explanation :

 grep -v

is a reverse grep. There are two patterns: [0-9] and -

| improve this answer | |
  • for your grep -Ev part. can I use '[^a-z]' because there might be other symbols – Ulysses Jan 31 '18 at 18:24
  • | grep -Ev '[^a-z]' – Ulysses Jan 31 '18 at 18:25

Two solutions:

A shell loop:

for name in /usr/bin/*file*; do
   case "${name##*/}" in
        *[!a-z]*) ;;
        *) printf '%s\n' "$name"

This first generates a list of pathnames under /usr/bin whose basename matches *file* and then tests whether those basenames also contains characters that are not lowercase letters. If no non-lowercase letters are found, the pathname is printed.

This would potentially also find names of directories that fulfil the criteria. If that is not wanted, just do

[ ! -f "$name" ] && continue

before the case statement to skip every name that is not a regular file.

Using GNU find:

find /usr/bin -maxdepth 1 -type f -name '*file*' -regex '.*/[a-z]*$'

This also does a two stage filtering. First on the basename which must match *file*, and then on the end of the pathname (-regex matches against the complete pathname) to make sure there's only lowercase letters there.

| improve this answer | |

You were close to the goal, just change the "word-boundary" (\<>) limits to the start of line and end of line (^$) limits and all will be fine:

$ ls /usr/bin | grep '^[a-z]*file[a-z]*$'

That is: starting from the start of each line find lines that contain any amount of lowercase letters (a-z) zero or more times followed by the word file and lowercase letters again until the end of the line.

Note that the above command will not work correctly for filenames with newlines.

| improve this answer | |
ls /usr/bin | grep '\<[a-z][^\-]*file[a-z][^\-]*\>'
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.