Это происходит для символа t и корня значения. Довольно недоумение
$ echo [s]
[s]
$ echo [t]
t
$ echo [ t ]
[ t ]
$ echo [root]
t
Это происходит для символа t и корня значения. Довольно недоумение
$ echo [s]
[s]
$ echo [t]
t
$ echo [ t ]
[ t ]
$ echo [root]
t
Не являясь оболочкой habitué (и не желая становиться), я нашел удивительным, как расширение имени файла должно вести себя, когда совпадений не найдено. Я сообщу Bash ссылку
Bash сканирует каждое слово для символов
*
,?
и[
. Если один из эти символы появляются, тогда слово рассматривается как шаблон, и заменяется алфавитно отсортированным списком имен файлов, соответствующих шаблон. Если совпадающие имена файлов не найдены:
- если опция оболочки
nullglob
отключена, слово остается неизменным- Если установлена опция оболочки
nullglob
, слово удаляется- Если установлена опция оболочки
failglob
, выводится сообщение об ошибке и команда не выполняется
Хорошая новость заключается в том, что эта вещь настраивается. Плохой - это script, который может потерпеть неудачу несколькими способами, которых вы не ожидаете - по крайней мере, я этого не сделал, и мне потребовалось некоторое время, чтобы понять, почему echo
ведет себя так, как вы это делали, просто чтобы найти, что это из-за комбинации странных имен файлов (кто когда-либо хочет назвать файл t
?), скрытая конфигурация (nullglob
отключена, опция по умолчанию, но все еще скрыта) и безобидная команда.
Я сказал безвредный, потому что это то, что вы получаете, например, когда цель ls
(сбой, потому что файл не найден):
[email protected]:~$ mkdir test
[email protected]:~$ cd test
[email protected]:~/test$ touch t
[email protected]:~/test$ ls [t]
t
[email protected]:~/test$ ls [v]
ls: cannot access [v]: No such file or directory
[]
обозначает класс символов, и у вас есть файл с именем t
в вашем текущем каталоге.
Далее следует пояснить следующее:
$ ls
$ echo [t]
[t]
$ touch t
$ echo [t]
t
$ echo [root]
t
$ touch r
$ echo [root]
r t
Если вы хотите повторить что-то в []
, отпустите [
:
echo \[$var]
Обратите внимание на разницу:
$ echo \[root]
[root]
или, как Гленн Джекман указывает,
$ echo '[root]'
[root]
$ echo "[root]"
[root]
Shell Command Language говорит, что следующие символы являются особыми для оболочки в зависимости от контекста:
* ? [ # ~ = %
Кроме того, следующие символы должны быть указаны, если они должны представлять себя:
| & ; < > ( ) $ ` \ " ' <space> <tab> <newline>
Вы также можете использовать printf
, чтобы определить, какие символы в заданном входе должны быть экранированы, если вы не указываете аргументы. Для вашего примера, т.е. [s]
:
$ printf "%q" "[s]"
\[s\]
Другой пример:
$ printf "%q" "[0-9]|[a-z]|.*?$|1<2>3|(foo)"
\[0-9\]\|\[a-z\]\|.\*\?\$\|1\<2\>3\|\(foo\)
[]
обозначает класс символов. Проще говоря, класс символов - это способ обозначить набор символов таким образом, чтобы соответствовать одному символу. [A-Z]
- очень распространенный пример - он соответствует всем алфавитам от A
до Z
.
Ниже приведены результаты команд в новом каталоге:
$ echo [s]
[s]
$ echo [t]
[t]
$ echo [ t ]
[ t ]
$ echo [root]
[root]
Как вы можете видеть, echo
отображает их как есть. Зачем? Прочтите следующий раздел.
Каждый раз, когда вы вводите команду в терминал и нажимаете клавишу ENTER, bash выполняет много операций внутри, прежде чем распечатывать результаты в оболочку. Простейшим примером является расширение *
:
$ echo *
evil_plans.txt dir1 dir2
Вместо вывода литерала *
в качестве вывода он распечатал содержимое каталога. Почему это случилось? Потому что *
имеет особое значение - это подстановочный знак, который может соответствовать любому символу имени файла. Важно отметить, что команда echo
вообще не видит *
- только расширенный результат.
Существуют разные виды расширений:
... и, вероятно, больше. В этом случае расширение имени файла является соответствующим типом расширения.
Когда вы вводите команду echo
и нажимаете ENTER, bash обрабатывает команду и разбивает ее на слова. Как только это будет сделано, он сканирует слова для следующих символов: ?
, *
и [
. Все это метасимволы и имеют особое значение. Если bash обнаруживает появление любого из этих символов, он рассматривает поставляемое слово как шаблон.
Например, рассмотрим следующий случай:
$ touch foobar foobak fooqux barbar boofar
$ echo foo*
foobar foobak fooqux
Как вы можете видеть, *
расширил и перечислил совпадающие имена файлов. (В этом случае те, которые начинаются с foo
.)
Теперь попробуйте еще один пример:
$ touch gray.txt grey.txt
$ echo gr?y.txt
gray.txt grey.txt
?
соответствует одному символу. Больше ничего. В этом случае gray.txt
и grey.txt
совпадали с шаблоном, поэтому оба были напечатаны.
Другой пример:
$ touch hello hullo hallo
$ echo h[aeu]llo
hallo hello hullo
Что здесь произошло? Как вам известно, [aeu]
- это класс символов. Класс символов соответствует именно одному из символов в нем; он никогда не соответствует более чем одному символу. В этом случае символы в классе символов могут корректно соответствовать именам файлов, поэтому результаты были распечатаны.
Если совпадающие имена файлов не найдены, слово остается неизменным.
$ echo [s]
[s]
является символьным классом и соответствует только одному символу - литералу s
. Но совпадающих файлов не найдено, поэтому оно было возвращено как есть.
$ echo [t]
[t]
также является символьным классом. Он соответствует одному символу. В вашем каталоге был файл с именем t
, то есть был матч. Таким образом, оно вернуло имя найденного имени файла.
$ echo [root]
[root]
соответствует следующим символам: r
, o
, t
. Как вы, наверное, догадались, o
, встречающийся дважды в классе символов, здесь не имеет значения. Класс символов может соответствовать только одному символу. Итак, echo [root]
попытается найти имена файлов, которые имеют соответствующий символ. Поскольку в вашем каталоге существует файл с именем t
, он указан в списке.
Всегда используйте цитаты и экранирование, если это необходимо. Они дают вам общий контроль над результатами синтаксического анализа, расширения и расширения.
Чтобы дополнить полезный ответ devnull:
Использование строки без кавычек в bash (в большинстве случаев) заставляет ее интерпретироваться как шаблон (выражение подстановочного выражения, свободно говорящее, отдаленный, примитивный родственник регулярного выражения).
В вашем случае шаблон сопоставляется с именами файлов и подпапок в текущей рабочей папке, поскольку @devnull демонстрирует: [root]
означает: соответствие любому файлу, имя которого состоит всего из 1 символ r
или o
(с указанием o
дважды является избыточным) или t
.
Это сопоставление шаблона с именами файлов называется расширением имени пути.
Обратите внимание, что также относится к ссылкам без кавычек, поэтому следующее даст тот же результат:
s='[t]' # Assign string _literal_ (since quoted) to variable.
echo $s # Content of $s, since unquoted, is now subject to pathname expansion.
Чтобы обрабатывать строку (выборочно) буквально, вы должны использовать цитирование.
В bash
есть три способа цитирования строк:
\
- укажите отдельные символы, которые имеют особое значение (так называемые метасимволы):
echo \[t\]
Это гарантирует, что эти другие специальные символы рассматриваются как литералы.
Вставьте строку в одинарные кавычки ('...'
):
echo '[t]'
Это защищает строку от любых расширений (интерпретации) оболочкой.
Предостережение: вы не можете включить сам '
в строку с одной кавычкой (даже с экранированием).
Вставьте строку в двойные кавычки ("..."
):
echo "[t]"
Это защищает строку от некоторых расширений оболочкой, выборочно разрешая другим.
В данном случае '[t]'
и "[t]"
ведут себя одинаково.
Однако использование "
позволяет ссылаться на переменные (расширение параметров), выполнять подстановки команд и выполнять вычисления (арифметическое расширение)
внутри строки, например:
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
Для списка всех расширений, выполняемых bash
, найдите раздел EXPANSION
в man bash
или посетите https://www.gnu.org/software/bash/manual/html_node/Shell-Expansions.html.