Differences between revisions 14 and 27 (spanning 13 versions)
Revision 14 as of 2010-09-25 17:07:24
Size: 9811
Editor: GreyCat
Comment: swapping Arrays and Tests&Conditionals
Revision 27 as of 2014-11-28 07:09:55
Size: 5
Editor: 148
Comment:
Deletions are marked like this. Additions are marked like this.
Line 1: Line 1:
## page was renamed from BashGuide/04.Patterns
[[BashGuide/Parameters|<- Parameters]] | [[BashGuide/TestsAndConditionals|Tests and Conditionals ->]]
----
== Patterns ==

[[BASH]] offers three different kinds of ''pattern matching''. Pattern matching serves two roles in the shell: selecting filenames within a directory, or determining whether a string conforms to a desired format.

On the command line you will mostly use ''globs''. These are a fairly straight-forward form of patterns that can easily be used to match a range of files, or to check variables against simple rules.

The second type of pattern matching involves ''extended globs'', which allow more complicated expressions than regular globs.

Since version `3.0`, [[BASH]] also supports ''regular expression'' patterns. These will be useful mainly in scripts to test user input or parse data.

--------
 . ''Pattern'': A pattern is a string with a special format designed to match filenames, or to check, classify or validate data strings.
--------



<<Anchor(Glob_Patterns)>>
=== Glob Patterns ===

[[glob|Globs]] are a very important concept in [[BASH]], if only for their incredible convenience. Properly understanding globs will benefit you in many ways. Globs are basically patterns that can be used to match filenames or other strings.

Globs are composed of normal characters and meta characters. Meta characters are characters that have a special meaning. These are the basic meta characters:

 * '''*''': Matches any string, including the null string.
 * '''?''': Matches any single character.
 * '''[...]''': Matches any one of the enclosed characters.

Globs are implicitly ''anchored'' at both ends. What this means is that a glob must match a ''whole'' string (filename or data string). A glob of `a*` will not match the string `cat`, because it only matches the `at`, not the whole string. A glob of `ca*`, however, would match `cat`.

Here's an example of how we can use glob patterns to expand to filenames:

{{{
    $ ls
    a abc b c
    $ echo *
    a abc b c
    $ echo a*
    a abc
}}}
[[BASH]] sees the glob, for example `a*`. It ''expands'' this glob, by looking in the current directory and matching it against all files there. Any filenames that match the glob, are enumerated and used in place of the glob. As a result, the statement `echo a*` is replaced by the statement `echo a abc`, and is then executed.

[[BASH]] performs filename expansions ''after'' word splitting has already been done; therefore, filenames generated by a glob will always be handled correctly. For example:

{{{
    $ touch "a b.txt"
    $ ls
    a b.txt
    $ rm *
    $ ls
}}}
Here, `*` is expanded into the single filename "`a b.txt`". This filename will be passed as a single argument to `rm`. It is important to understand that using globs to enumerate files is '''always''' a better idea than using {{{`ls`}}} for that purpose. Here's an example with some more complex syntax which we will cover later on, but it will illustrate the reason very well:

{{{
    $ ls
    a b.txt
    $ for file in `ls`; do rm "$file"; done
    rm: cannot remove `a': No such file or directory
    rm: cannot remove `b.txt': No such file or directory
    $ for file in *; do rm "$file"; done
    $ ls
}}}
Here we use the `for` command to go through the output of the `ls` command. The `ls` command prints the string `a b.txt`. The `for` command splits that string into words over which it iterates. As a result, `for` iterates over first `a`, and then `b.txt`. Naturally, this is '''not''' what we want. The glob, however, expands in the proper form. It results in the file "`a b.txt`", which `for` takes as a single argument.

[[BASH]] also supports a feature called `Extended Globs`. These globs are more powerful in nature; technically, they are equivalent to regular expressions, although the syntax looks different than most people are used to. This feature is turned off by default, but can be turned on with the `shopt` command, which is used to toggle '''sh'''ell '''opt'''ions:

{{{
    $ shopt -s extglob
}}}
 * '''?(list)''': Matches zero or one occurrence of the given patterns.
 * '''*(list)''': Matches zero or more occurrences of the given patterns.
 * '''+(list)''': Matches one or more occurrences of the given patterns.
 * '''@(list)''': Matches one of the given patterns.
 * '''!(list)''': Matches anything except one of the given patterns.
The list inside the parentheses is a list of regular or extended globs separated by the `|` character. Here's an example:

{{{
    $ ls
    names.txt tokyo.jpg california.bmp
    $ echo !(*jpg|*bmp)
    names.txt
}}}
Our glob now expands to anything that does not match the `*jpg` or the `*bmp` pattern. Only the text file passes for that, so it is expanded.

In addition to filename expansion, globs may also be used to check data matches a specific format. For example, we might be given a filename, and need to take different actions depending on its extension:
{{{
    $ filename="somefile.jpg"
    $ if [[ $filename = *.jpg ]]; then
    > echo "$filename is a jpeg"
    > fi
    somefile.jpg is a jpeg
}}}

The `[[` keyword and the `case` builtin command (which we will discuss in more detail later) both offer the opportunity to check a string against a glob -- either regular globs, or extended globs, if the latter have been enabled.

Then, there is ''Brace Expansion''. Brace Expansion technically does not fit in the category of Globs, but it is similar. Globs only expand to actual filenames, where brace expansion will expand to any permutation of the pattern. Here's how they work:

{{{
    $ echo th{e,a}n
    then than
    $ echo {/home/*,/root}/.*profile
    /home/axxo/.bash_profile /home/lhunath/.profile /root/.bash_profile /root/.profile
    $ echo {1..9}
    1 2 3 4 5 6 7 8 9
    $ echo {0,1}{0..9}
    00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19
}}}
--------
 . '''Good Practice: <<BR>> You should always use globs instead of `ls` (or similar) to enumerate files. Globs will always expand safely and minimize the risk for bugs. <<BR>> You can sometimes end up with some very weird filenames. Most scripts aren't tested against all the odd cases that they may end up being used with. Don't let your script be one of those!'''
----
 . '''In The Manual: [[http://www.gnu.org/software/bash/manual/bashref.html#Pattern-Matching|Pattern Matching]]'''
----
 . '''In the FAQ: <<BR>> [[BashFAQ/016|How can I use a logical AND/OR/NOT in a shell pattern (glob)?]]'''
----
 . ''Glob'': A glob is a string that can match certain strings or filenames.
--------



<<Anchor(Regular_Expressions)>>
=== Regular Expressions ===

Regular expressions (regex) are similar to ''Glob Patterns'' but cannot be used for filename matching in [[BASH]]. Since 3.0, [[BASH]] supports the `=~` operator to the `[[` keyword. This operator matches the string that comes before it against the regex pattern that follows it. When the string matches the pattern, `[[` returns with an exit code of `0` ("true"). If the string does not match the pattern, an exit code of `1` ("false") is returned. In case the pattern's syntax is invalid, `[[` will abort the operation and return an exit code of `2`.

[[BASH]] uses the ''Extended Regular Expression'' (`ERE`) dialect. We will not cover regexes in depth in this guide, but if you are interested in this concept, please read up on RegularExpression, or [[http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap09.html#tag_09_04|Extended Regular Expressions]].

''Regular Expression'' patterns that use capturing groups (parentheses) will have their captured strings assigned to the `BASH_REMATCH` variable for later retrieval.

Let's illustrate how regex can be used in [[BASH]]:

{{{
    $ if [[ $LANG =~ (..)_(..) ]]
    > then echo "You live in ${BASH_REMATCH[2]} and speak ${BASH_REMATCH[1]}."
    > else echo "Your locale was not recognised"
    > fi
}}}
Be aware that regex parsing in [[BASH]] has changed between releases `3.1` and `3.2`. Before `3.2` it was safe to wrap your regex pattern in quotes but this has changed in `3.2`. Since then, regex should always be unquoted. You should protect any special characters by escaping it using a backslash.

{{{
    $ [[ "My sentence" =~ My\ sentence ]]
}}}
Be careful to escape any characters that the shell could misinterpret, such as whitespace, dollar signs followed by text, braces, etc.

--------
 . '''Good Practice: <<BR>> Since the way regex is used in `3.2` is also valid in `3.1` we ''highly'' recommend you just never quote your regex. Remember to keep special characters properly escaped!'''
 . ''' For cross-compatibility (to avoid having to escape parentheses, pipes and so on) use a variable to store your regex, e.g. {{{re='^\*( >| *Applying |.*\.diff|.*\.patch)'; [[ $var =~ $re ]]}}} This is much easier to maintain since you only write ERE syntax and avoid the need for shell-escaping, as well as being compatible with all 3.x BASH versions.'''
 . See also [[http://tiswww.case.edu/php/chet/bash/FAQ|Chet Ramey's Bash FAQ]], section E14.
----
 . '''In The Manual: [[http://www.daemon-systems.org/man/regex.3.html|Regex(3)]]'''
----
 . '''In the FAQ: <<BR>> [[BashFAQ/066|I want to check if [[ $var == foo or $var == bar or $var == more ... without repeating $var n times.]]'''
----
 . ''Regular Expression'': A regular expression is a more complex pattern that can be used to match specific strings (but unlike globs cannot expand to filenames).
--------
[[BashGuide/Parameters|<- Parameters]] | [[BashGuide/TestsAndConditionals|Tests and Conditionals ->]]
cas

cas

BashGuide/Patterns (last edited 2016-01-15 10:08:43 by google-proxy-66-249-93-205)