0

There is a similar question in stackoverflow, but it does not work with bash.

What is needed to make it work with bash, to use with scripts/variables?

I'm talking about the ISO 639-1 or ISO 639-2 codes.

My bash is GNU 4.3.

Example:

root@box ~/test2 # ls
eng  en-US  por pt-BR

The regex in that pages fails:

root@box ~/test2 # ls | grep ^[a-z]{2}-[A-Z]{2}$
root@box ~/test2 # ls | grep ^[A-Za-z]{1,8}(-[A-Za-z0-9]{1,8})*$
-bash: syntax error near unexpected token `('
root@box ~/test2 # ls | grep ^[a-z]{2}(-[A-Z]{2})*
-bash: syntax error near unexpected token `('
root@box ~/test2 # ls | grep "^[a-z]{2}(-[A-Z]{2})*"
5
  • Can you show the actual script you tried and which didn't work?
    – ilkkachu
    Jan 15 '18 at 8:57
  • The expression in that answer works just fine with grep, as your comment there suggests you wanted, but here you've said you want it in Bash. Which is it? Jan 15 '18 at 8:57
  • @Freedo, edit your question to show the script you've tried.
    – ilkkachu
    Jan 15 '18 at 9:02
  • How can I build a script based on this if I don't even have the regex yet? I'm just literally doing ls | grep "their regex". And none in that page worked for me. You must be doing something different?
    – Freedo
    Jan 15 '18 at 9:03
  • You just need to quote the regex. Jan 15 '18 at 9:15
4

{n} without backslashes is part of extended regular expressions, so you need the -E flag for grep. Also, you want to quote the regex, since as you saw, the parenthesis and braces have special meaning to the shell.

$ ls
eng  en-US  por  pt-BR

$ printf "%s\n" * | grep -E '^[a-z]{2}-[A-Z]{2}$'
en-US
pt-BR

$ printf "%s\n" * | grep -E '^[A-Za-z]{1,8}(-[A-Za-z0-9]{1,8})*$'
eng
en-US
por
pt-BR

Or with just Bash:

$ for f in * ; do 
   [[ $f =~ ^[A-Za-z]{1,8}(-[A-Za-z0-9]{1,8})*$ ]] && printf "%s\n" "$f" ; done
eng
en-US
por
pt-BR

(The [[ .. ]] test construct is special, the braces and parenthesis have a different meaning inside it, and in fact the regex must be unquoted here. Note that this is totally different from [ .. ]. See e.g. BashGuide on conditionals)


The [a-zA-Z]{1,8} part matches strings of up to eight letters, and the final * allows an arbitrary number of repetitions of the group in parenthesis, so this would match something like foobar-foobar-foobar too.

We could change the pattern to ^[A-Za-z]{2,3}(-[A-Za-z0-9]{2,3})?$ only allow codes with two or three letters and just one tailing -xx part, if that's all you need.


Unquoted {1,8} is brace expansion:

$ echo ^[A-Za-z]{1,8}
^[A-Za-z]1 ^[A-Za-z]8

and unquoted [] is a filename pattern match (glob)...

$ touch "^a1" "^b8"
$ echo ^[A-Za-z]{1,8}
^a1 ^b8
4
  • You are 3 seconds faster than me! Respect! Jan 15 '18 at 9:18
  • Wow so much different ways to do it, thanks guys...I'm so dumb, whenever I ask something here I feel so dumb afterwards because you make seems so easy. Don't even know what one to accept. But thanks for both!
    – Freedo
    Jan 15 '18 at 9:26
  • Also, I noticed that matching things like por and eng result in a lot of false positives. I assume this is not something easy to be fixed, so I'll try to just use the 4 letter language codes.
    – Freedo
    Jan 15 '18 at 9:35
  • @Freedo, depends on what the other files are. You can modify the numbers in [a-z]{1,8}, they set the number of letters that part of the expression matches.
    – ilkkachu
    Jan 15 '18 at 9:42
2

First you need to quote the regex as per grep requirements using grep 'regex'

Then either you will use extended regex support in grep using egrep or grep -E and this will work ok:

$ ls | egrep '^[a-z]{2}-[A-Z]{2}$'

or you can use classic grep - basic regex but you need to escape { and }:

$ ls | grep '^[a-z]\{2\}-[A-Z]\{2\}$'

For these data as per your question

$ ls
eng
en-US
main.sh
por
pt-BR

Output in both grep cases will be

en-US
pt-BR
1
  1. This is a extended regular expression, so use -E
  2. If you use search pattern with special chars, quote them

grep -E "^[a-z]{2}-[A-Z]{2}$"

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.