Если у меня есть эта команда:

expr 2 * 4

Это не сработает, потому что будет оцениваться подстановочный знак *, и поэтому мне нужно его избежать:

expr 2 \* 4

Но если у меня есть следующие два случая:

let var1=2*4

var2=$((2*4))

Тогда подстановочный знак * не будет оцениваться, и поэтому нет необходимости его экранировать.

Почему подстановочный знак * не был оценен в двух приведенных выше случаях? я предполагаю, что bash просто не оценивает его при использовании let и $(()).

Если это так, существуют ли другие ситуации, когда подстановочные знаки не оцениваются?

0
user267498 26 Дек 2017 в 03:42
Пробел вокруг '*' заставил его оцениваться по-разному
 – 
guiverc
26 Дек 2017 в 03:54

3 ответа

В var2=2*4 из-за пробелов (метасимволов) и отсутствия файла, начинающегося с 2, некоторых символов и 4 в pwd.

Строка разбита на токены с использованием пробелов в качестве разделителей.

В нем нет пробелов (и после = он считается заключенным в кавычки):

$ var1=2*4

Но это действительно так:

$ echo 2 * 4    

И * становится токеном и расширяется (как расширение имени файла).

Чтобы избежать особого эффекта *, используйте кавычки:

$ expr 2 \* 4
8
$ expr 2 "*" 4
8
$ expr 2 '*' 4
8

Однако это расширяет:

$ touch 2good4
$ echo 2*4
2good4

В echo $((2*4)) то, что находится внутри конструкции $((…)), интерпретируется как арифметическое расширение. любое из этого будет делать то же самое (даже с пробелами):

$ echo $((2*4))
8
$ echo $(( 2 * 4 ))
8
$ echo $((    2   *    4    ))
8
1
muru 26 Дек 2017 в 06:27

Поведение, которое вы видите, объясняется здесь для подстановки и здесь для расширения и страницы man let для спецификации параметра, которую bash знает, чтобы передать 'let'

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

echo 2 * 4     #echo 2, the contents of the pwd and then 4

Если * предшествует маркер, указывающий на использование разностной формы или раскрытия (математика/команда/параметр или вообще без раскрытия), тогда используется соответствующий метод. Так

let var1=2*4   #pass the **arithmetic** argument var1=2*4 to the let command because bash knows let takes special characters (% * etc) as arguments

var2=$((2*4))  #bash parses the string as far as $ and sees an instruction to expand
               #bash then sees (( the which specifies arithmetic expansion specifies

И как ты сказал

echo 2 \* 4   #perform no expansion
1
bu5hman 26 Дек 2017 в 07:34

Если вы хотите знать причину, вы должны знать, что * может сделать в bash и как bash разбирает команды:

Во-первых, что * может сделать в bash:

  1. как подстановочный знак в имени файла

  2. умножить

  3. 0 или более раз в регулярном выражении

    ...

И как команды разбора bash (значительно упрощенные, на самом деле очень сложные):

  1. подстановка параметров

  2. подстановка команд

  3. подстановка

    ...

Предположим, у вас есть a.txt b.txt в текущем каталоге, в expr 2 * 4, после подстановки параметра -> подстановка команды -> globbing, он станет

expr 2 a.txt b.txt 4

Который по причине не вернет правильный ответ.

Однако в expr 2 \* 4 * — это символ! Ни одна из подстановок параметров, подстановок команд, подстановок не повлияет на это. поэтому выражение получает 3 параметра 2 * 4 и возвращает 8.

В let var1=2*4 bash выполняет арифметическую подстановку перед подстановкой, команда после промежуточного переноса — let var1=8. Если у вас есть 2a4 2b4 в текущем каталоге и вы не используете let, просто

var1=2*4
echo $var1

Он вернет 2a4 2b4, а НЕ 8.

В var2=$((2*4)) bash также выполняет арифметическую замену, поскольку (()) является арифметическим оператором.

PS: Если вы хотите, чтобы * был арифметическим оператором, сохраните его в (()) $[] или используйте let, например:

((var1 = 2 * 4)) or var1=$((2 * 4))
var1=$[2 * 4]

ПРОБЕЛЫ разрешены!

0
Community 11 Июн 2020 в 17:16