How to find files that only contain alphanumeric characters and [áéíóúñ.,¿?¡!()]?

I have some sparse plain text files and I need to separate them from scripts and any other thing, they're just poetries in spanish so it's unlikely that the contain [#></:] for example. I came with

sudo find . -type f -not -path '*/.??*/*' -exec file {}  \; \
  | grep ": Unicode text, UTF-8 text"$ \
  | cut -d: -f1 \
  | while read file; do 
     grep -iv '[a-z0-9\.\/_\-áéíóúñ]' "$file" || echo $file

but it matches lines and I need to match the entire file.

Edit: At least what worked for me was:

sudo find . -type f -not -path "*/.Trash-*/*" -not -path '*/.??*/*' -exec file {} \;|
grep ": Unicode text, UTF-8 text"$|
cut -d: -f1|
while read file do 
grep -ivq "^[a-z0-9\.\/_\-\ \,\"áéíóúñ\!¿¡?\(\)]*$" "$file"||
echo "$file">>/tmp/textlocation ; done

The trivial solution of adding ^ and $ did the difference. The -L solution listed here may work and is more elegant but mine solution did the job.

  • Do you have many extensions of files? Commented Oct 12, 2021 at 21:58
  • @Quasímodo, yeah, it's my own and I don't use semicolons, anyway I can Improve myself the answers if something is missing but suere it will not contain # : or / Commented Oct 12, 2021 at 22:15
  • Do you also want to allow blanks and newlines?
    – Kusalananda
    Commented Oct 13, 2021 at 6:06
  • @they yes poetry have them between verses Commented Oct 16, 2021 at 21:14

2 Answers 2


You can use GNU grep instead if I understand you correctly, searching the files that don't match the [#></:] set of characters:

$ grep -Lr '[#></:]'
  • -L will list the files that do not match the pattern

    -L, --files-without-match
    Suppress normal output; instead print the name of each input file from which no output would normally have been printed. The scanning will stop on the first match.

  • -r will search recursively

    -r, --recursive
    Read all files under each directory, recursively, following symbolic links only if they are on the command line. Note that if no file operand is given, grep searches the working directory. This is equivalent to the -d recurse option.

You can also add the -Z option in case you need to perform some action on the files other than listing then.

  • 2
    +1, much better than my idea (which was to use perl -0). You could speed that up with -m 1 to skip to the next file on the first line in that matches the unwanted pattern - it would skip scripts, for example, after the first line (#!). Also, probably should be a bracket expression [#></:] otherwise grep will try to match that exact sequence of characters instead of any one of them. And maybe use -Z for NUL-separated output if the script is going to do anything with the files other than just list them.
    – cas
    Commented Oct 13, 2021 at 1:02
  • @cas added your suggestions (and correction) to the answer, thanks! Commented Oct 13, 2021 at 9:38
  • hum, is this right? If I've understood correctly, -v looks for lines not matching the pattern, and then -l prints files where any line meets the condition. And with -v, that's files that have at least one line that does not match the pattern. E.g. printf '%s\n' 'foo' '::' > test.txt and the grep would match test.txt. You'd need -L/--files-without-match from GNU grep. Or a shell loop around grep -q, inverting the sense. Or -z and the assumption that the files don't have NULs, and hence just one NUL-terminated "line".
    – ilkkachu
    Commented Oct 13, 2021 at 9:55
  • @ilkkachu thanks! I edited the answer with the GNU -L option. Commented Oct 13, 2021 at 10:19
  • @cas according to the (GNU) manual both -l and -L stop scanning after the first match, so -m1 wouldn't be needed. Commented Oct 13, 2021 at 10:24

I would try the -q option : « Quiet; do not write anything to standard output. Exit immediately with zero status if any match is found, even if an error was detected. »

This avoids printing anything and enable you to get the return code.

Note also that an alphanumeric only line is "^[a-z0-9\.\/_\-áéíóúñ]*$" (With a ^, * and $) Then I would type

grep -ivq "^[a-z0-9\.\/_\-áéíóúñ]*$" || echo $file

You may need to add some characters : spaces, comma… if you don’t, many poetries wont pass !

  • 1
    Note that within a double-quoted string, things like \., \/, and \- expands to ., /, and -. Note also that the - then probably defines a range of characters between _ and á, which may not make sense. Also, the \ character itself is literal within [...], as are . and /.
    – Kusalananda
    Commented Oct 13, 2021 at 9:48
  • 1
    @they, no, double-quotes leave the backslashes except in front of the few characters that are special within double-quotes. (dquotes, backslashes, dollars and backticks, I think.) "\." is the same as '\.' or \\., but of course "\$" removes the backslash and is the same as '$' or \$. The dash might be suspect though, I'm not sure you can escape it inside brackets in BRE. (Perl-style regexes are different.) If I didn't get lost in the negations, I think that command accepts the backslash the same as letters, but not the dash. '^[-a-z0-9./_áéíóúñ]*$' might be better.
    – ilkkachu
    Commented Oct 13, 2021 at 10:03
  • 1
    @ilkkachu Hm. Yes. I'm getting the quoting rules confused. In any case, the use of backslashes within [...] is obviously not doing what the user thinks they do.
    – Kusalananda
    Commented Oct 13, 2021 at 10:06

You must log in to answer this question.

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