Why bash "echo [t]" leads to "t" not to "[t]"

This happens for the symbol t and the root of the value. Quite perplexed

$ echo [s] [s] $ echo [t] t $ echo [ t ] [ t ] $ echo [root] t 
+43
bash
Apr 17 '14 at 18:15
source share
4 answers

Not being a habituΓ© shell (and not wanting to become), I found it amazing how the file name extension should behave when no matches were found. I will inform Bash link

Bash scans every word for characters * ? and [ . If one of these characters appears, then the word is treated as a pattern, and is replaced by an alphabetically sorted list of file names matching the pattern. If no matching file names are found:

  • if the nullglob shell option is disabled, the word remains unchanged
  • If the nullglob shell option is nullglob , the word is deleted
  • If the failglob shell option is failglob , an error message is displayed and the command fails

The good news is that this thing is customizable. The bad one is a script that can fail in several ways you don't expect - at least I didn't, and it took me a while to figure out why echo behaves the way you did, just to find what is it because of a combination of strange file names (who ever wants to name the file t ?), hidden configuration ( nullglob disabled, the default option, but still hidden) and a harmless command.

I said harmless, because this is what you get, for example, when the target is ls (crashes because the file was not found):

 raffaele@Aldebaran:~$ mkdir test raffaele@Aldebaran:~$ cd test raffaele@Aldebaran:~/test$ touch t raffaele@Aldebaran:~/test$ ls [t] t raffaele@Aldebaran:~/test$ ls [v] ls: cannot access [v]: No such file or directory 
+14
Apr 18 '14 at 21:21
source share

[] denotes a character class, and you have a file named t in your current directory.

The following should be clarified:

 $ ls $ echo [t] [t] $ touch t $ echo [t] t $ echo [root] t $ touch r $ echo [root] rt 



If you want to repeat something in [] , release [ :

 echo \[$var] 

Please note the difference:

 $ echo \[root] [root] 

or, as Glenn Jackman points out,

 $ echo '[root]' [root] $ echo "[root]" [root] 



Shell Command Language says the following characters are context-specific:

 * ? [ # ~ = % 

In addition, the following characters must be indicated if they are to represent themselves:

 | & ; < > ( ) $ ` \ " ' <space> <tab> <newline> 



You can also use printf to determine which characters in a given input should be escaped if you do not specify arguments. For your example, i.e. [s] :

 $ printf "%q" "[s]" \[s\] 

Another example:

 $ printf "%q" "[0-9]|[az]|.*?$|1<2>3|(foo)" \[0-9\]\|\[az\]\|.\*\?\$\|1\<2\>3\|\(foo\) 
+62
Apr 17 '14 at 18:17
source share

[] denotes a character class. Simply put, a character class is a way to designate a character set in a way that matches a single character . [AZ] is a very common example - it matches all alphabets from A to Z

The following are the results of the commands in the new directory:

 $ echo [s] [s] $ echo [t] [t] $ echo [ t ] [ t ] $ echo [root] [root] 

As you can see, echo displays them as is. What for? Read the next section.

Expansion

Each time you type a command into a terminal and press the ENTER key, bash performs many operations internally before printing the results to the shell. The simplest example is the * extension:

 $ echo * evil_plans.txt dir1 dir2 

Instead of printing the literal * as output, he printed the contents of the directory. Why did this happen? Because * has a special meaning - it is a wildcard that can match any character in the file name. It is important to note that the echo command does not see * at all - only an extended result.

There are different types of extensions:

  • Bracket Extension
  • Tilda Extension
  • Parameter Extension
  • Team extension
  • Arithmetic expansion
  • Process replacement
  • File name extension

... and probably more. In this case, the file name extension is the appropriate type of extension.

File name extension

When you enter the echo command and press ENTER, bash processes the command and breaks it into words. Once this is done, it scans words for the following characters ? , * and [ . All these are metacharacters and have special meaning. If bash detects any of these characters to appear, it treats the supplied word as a pattern.

For example, consider the following case:

 $ touch foobar foobak fooqux barbar boofar $ echo foo* foobar foobak fooqux 

As you can see, * expanded and listed matching file names. (In this case, those starting with foo .)

Now try another example:

 $ touch gray.txt grey.txt $ echo gr?y.txt gray.txt grey.txt 

? matches one character. Nothing more. In this case, gray.txt and grey.txt same as the template, so both were printed.

Another example:

 $ touch hello hullo hallo $ echo h[aeu]llo hallo hello hullo 

What happened here? As you know, [aeu] is a character class. A character class corresponds to just one of the characters in it; it never matches more than one character. In this case, the characters in the character class may correctly match the file names, so the results were printed.

If no matching file names are found, the word remains unchanged.

Explanation for your specific case

 $ echo [s] 

[s] is a character class and corresponds to only one character - the literal s . But no matching files were found, so it was returned as is.

 $ echo [t] 

[t] also a character class. It matches a single character. In your directory there was a file called t , that is, there was a match. Thus, it returned the name of the found file name.

 $ echo [root] 

[root] matches the following characters: r , o , t . As you probably guessed, o , which occurs twice in the character class, does not matter here. A character class can match only one character. So echo [root] will try to find the names of files that have the corresponding character. Since there is a file named t in your directory, it is listed.

How to avoid such quirks?

Always use quotes and escapes if necessary. They give you general control over the results of parsing, expansion, and expansion.

+26
Apr 17 '14 at 23:48
source share

To complement devnull's useful answer :

Using a quote-free string in bash (in most cases) forces it to be interpreted as a pattern (wildcard expression, fluent, distant, primitive relative of the regular expression).

In your case, the pattern is mapped to the file and subfolder names in the current working folder , because @devnull shows: [root] means: matches any file whose name consists of only 1 r or o character (with o twice as redundant) or t . This pattern matching with file names is called a path name extension .

Note that this also applies to links without quotes , so the following will give the same result:

 s='[t]' # Assign string _literal_ (since quoted) to variable. echo $s # Content of $s, since unquoted, is now subject to pathname expansion. 

To process a string (selectively) literally, you must use quoting.

There are three ways to quote strings in bash :




\ - specify individual characters that have special meaning (the so-called metacharacters):

echo \[t\]

This ensures that these other special characters are treated as literals.




Paste the string in single quotes ( '...' ):

echo '[t]'

This protects the string from any extension (interpretation) by the shell.
Caution : you cannot include ' itself in a string with one quote (even with escaping).




Insert the string in double quotation marks ( "..." ):

echo "[t]"

This protects the string from some extensions by the shell, selectively allowing others.

In this case, '[t]' and "[t]" behave identically.

However, using " allows you to reference variables (parameter expansion), perform command substitutions, and perform calculations (arithmetic expansion) inside a string, for example:

echo "Home, sweet $HOME." # reference to variable $HOME; parameter expansion

echo "Today date and the current time are: $(date)" # command substitution

echo "Let put $(( 2 + 2 )) together." # arithmetic expansion

For a list of all the extensions that bash runs, find the EXPANSION section in man bash or visit https://www.gnu.org/software/bash/manual/html_node/Shell-Expansions.html .

+9
Apr 17 '14 at 18:41
source share



All Articles