Shell expansions
While working with shell, we perform a lot of similar and repetitive tasks. For example, in the current directory, there are 100 files but we are interested only in shell script whose file extension is .sh
. We can execute following command to view only shell script files in current directory:
$ ls *.sh
This will show all the files ending with .sh
. An interesting take away from here is the *
wildcard. It means a match list of files whose name can be anything and that ends with .sh
.
Shell expands all wildcard patterns. A list of the latest wildcard patterns are as follows:
- ~ (Tilde)
- * (Asterisk)
- ? (Question mark)
- [ ] (Square brackets)
- { } (Curly brackets)
To explain shell expansion for different wildcards, we will create a test folder in our home
directory using the mkdir
command containing different files mentioned as follows:
$ mkdir ~/test && cd ~/test $ touch a ab foo bar hello moo foo.c bar.c moo.c hello.txt foo.txt bar.sh hello.sh moo.sh
The touch
command creates an empty file if it doesn't exist. If a file exists, then the file timestamp changes:
$ ls a ab bar bar.c bar.sh foo foo.c foo.txt hello hello.sh hello.txt moo moo.c moo.sh
Running the preceding commands will create a test directory, and inside test directory creates files given as parameter to the touch
command.
~ (Tilde)
~ (Tilde) gets expanded by bash
when it is present at the beginning of an unquoted string. The expansion depends upon what tilde-prefix
is used. Tilde prefixes are characters until the first unquoted (/
) slash. Some of the bash
expansions are as follows:
~
: This is the user's home directory; the value is set in the$HOME
variable~user_name
: This is the home directory of the user'suser_name
~user_name
/file_name
: This is the file/directoryfile_name
in the user'suser_name
home directory~/file_name
: This is the file/directoryfile_name
in the home directory that is$HOME
/file_name
~+
: This is the current working directory; the value is set in the$PWD
variable~-
: This is the old or last working directory; the value is set in the$OLDPWD
variable~+/file_name
: This is the file/directoryfile_name
in the current directory that is$PWD/file_name
~-/file_name
: This is the file/directoryfile_name
in the old/last working directory that is$OLDPWD/file_name
* (Asterisk)
It matches zero or more characters. Take a test directory as an example:
- Display all files as follows:
$ ls * a ab bar bar.c bar.sh foo foo.c foo.txt hello hello.sh hello.txt moo moo.c moo.sh
- Display the C source files as follows:
$ ls *.c bar.c foo.c moo.c
- Display files that have
a
in its name, as follows:$ ls *a* a ab bar bar.c bar.sh
- Deleting files with an extension .txt as follows:
$ rm *.txt $ ls a ab bar bar.c bar.sh foo foo.c hello hello.sh moo moo.c moo.sh
? (Question mark)
It matches any single character: ? (single question mark will match a single character), ?? (double question mark matches any two characters), and so on. Take a test directory as an example:
$ touch a ab foo bar hello moo foo.c bar.c moo.c hello.txt foo.txt bar.sh hello.sh moo.sh
This will recreate files that were removed during the previous example, and also update the access and modification time of the existing files:
- Get files whose name length is irrespective of what the extension file has:
$ ls ?? ab
- Get files whose name length is 2 or 5:
$ ls ?? ????? ab bar.c foo.c hello moo.c
- Delete files whose name is four characters long:
$ rm ???? rm: cannot remove '????': No such file or directory This error is because there is no file name with 4 character
- Move files to the
/tmp
directory whose name is at least three characters long:$ mv ???* /tmp $ ls a ab
We see only two files in the test directory because the rest of the files were of the length 3 or more.
[ ] (Square brackets)
Square brackets match any character from the characters mentioned inside the square brackets. Characters can be specified as a word or range.
A range of characters can be specified using - (hyphen). For example:
[a-c]
: This matches a, b, or c[a-z]
: This matches any character from a to z[A-Z]
: This matches any character from A to Z[0-9]
: This matches any character from 0 to 9
Take a test directory as an example and recreate files in a test directory:
$ touch a ab foo bar hello moo foo.c bar.c moo.c hello.txt foo.txt bar.sh hello.sh moo.sh
Get files whose name starts with a
, b
, c
, or d
with the following command:
$ ls [a-d]* a ab bar bar.c bar.sh
Get files whose name starts with any letter and ends with a letter o
or h
, with the following command:
$ ls [a-zA-Z]*[oh] foo hello hello.sh moo moo.sh
Get files that have at least the letter o
twice in its name, with the following command:
$ ls *[o]*[o]* foo foo.c foo.txt moo moo.c moo.sh
[!characters]
(Exclamation mark) is used to match a character that is not part of a charter set mentioned inside square brackets.
Get files that don't have a number in its name, with the following command:
$ ls [!0-9]* a ab bar bar.c bar.sh foo foo.c foo.txt hello hello.sh hello.txt moo moo.c moo.sh
{ } (Curly brackets)
It creates multiple wildcard patterns to match. A brace expression may contain either a comma-separated list of strings, a range, or a single character.
A range can be specified by using the following:
{a..z}
: This matches all the charterer from a to z{0..6}
: This matches numbers 0, 1, 2, 3, 4, 5 and 6
Take a test directory as an example and recreate files in the test directory:
$ touch a ab foo bar hello moo foo.c bar.c moo.c hello.txt foo.txt bar.sh hello.sh moo.sh
Get files that have the file extension .sh
or .c
, with the following command:
$ ls {*.sh,*.c} bar.c bar.sh foo.c hello.sh moo.c moo.sh
Copy bar.c
to bar.html
by using the following command:
$ cp bar{.c,.cpp} # Expands to cp bar.c bar.cpp $ ls bar.* bar.c bar.cpp bar.sh
Print the number from 1
to 50
by using the following command:
$ echo {1..50} 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
Create 10 files that start with hello
and has an extension .cpp
:
$ touch hello{0..9}.cpp $ ls *.cpp hello0.cpp hello1.cpp hello2.cpp hello3.cpp hello4.cpp hello5.cpp hello6.cpp hello7.cpp hello8.cpp hello9.cpp
To avoid shell expansion of a wildcard, use backslash (\) or write a string within a single quote (' ').