Подтвердить что ты не робот

Почему bash "echo [t]" приводит к "t" не "[t]"

Это происходит для символа t и корня значения. Довольно недоумение

$ echo [s]
[s]
$ echo [t]
t
$ echo [ t ]
[ t ]
$ echo [root]
t
4b9b3361

Ответ 1

Не являясь оболочкой 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

Ответ 2

[] обозначает класс символов, и у вас есть файл с именем 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\)

Ответ 3

[] обозначает класс символов. Проще говоря, класс символов - это способ обозначить набор символов таким образом, чтобы соответствовать одному символу. [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, он указан в списке.

Как избежать таких причуд?

Всегда используйте цитаты и экранирование, если это необходимо. Они дают вам общий контроль над результатами синтаксического анализа, расширения и расширения.

Ответ 4

Чтобы дополнить полезный ответ 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.