2

Общий вопрос:

В Bash я знаю, что использовать переменную myvar можно двумя способами:

# Define a variable:
bash$ myvar="two words"

# Method one to dereference:
bash$ echo $myvar
two words

# Method two to dereference:
bash$ echo "$myvar"
two words

В случае выше, поведение идентично. Это из-за того, как работает echo . В других утилитах Unix слияние слов и двойных кавычек будет иметь огромное значение:

bash$ myfile="Cool Song.mp3"
bash$ rm "$myfile"            # Deletes "Cool Song.mp3".
bash$ rm $myfile              # Tries to delete "Cool" and "Song.mp3".

Мне интересно, каково более глубокое значение этого различия. Самое главное, как я могу точно увидеть, что будет передано команде, чтобы я мог видеть, правильно ли она заключена в кавычки?

Конкретный нечетный пример:

Я просто напишу код с наблюдаемым поведением:

bash$ mydate="--date=format:\"%Y-%m-%d T%H\""
bash$ git log "$mydate"    # This works great.
bash$ git log $mydate
fatal: ambiguous argument 'T%H"': unknown revision or path not in the working tree.

Зачем мне двойные кавычки? Что именно видит git-log после разыменования переменной без двойных кавычек?

Но теперь посмотрим на это:

bash$ nospace="--date=format:\"%Y-%m-%d\""
bash$ git log $nospace        # Now THIS works great.
bash$ git log "$nospace"      # This kind of works, here is a snippet:

# From git-log output:
Date:   "2018-04-12"

Фу, почему печатный вывод теперь содержит двойные кавычки? Похоже, что если двойные кавычки не нужны, они не удаляются, они интерпретируются как буквальные символы кавычек тогда и только тогда, когда они не нужны.

Что такое Git в качестве аргумента? Хотел бы я знать, как это выяснить.

Чтобы сделать вещи более сложными, я написал скрипт на Python с использованием argparse который просто печатает все аргументы (как их интерпретировал Bash, так и с литералами в двойных кавычках, где Bash считает их частью аргумента, и со словами, сгруппированными или не сгруппированными как Bash считает нужным), а скрипт Python argparse ведет себя очень рационально. К сожалению, я думаю, что argparse может молча исправлять известную проблему с Bash и, таким образом, скрывать запутанные вещи, которые Bash передает ему. Это всего лишь предположение, я понятия не имею. Может быть, git-log тайно испортит то, что Bash передает ему.

Или, может быть, я просто не знаю, что происходит вообще.

Благодарю.

Отредактировано Редактировать: Позвольте мне сказать это сейчас, прежде чем есть какие-либо ответы: я знаю, что я могу, возможно, использовать одинарные кавычки вокруг всего этого, а затем не избежать двойных кавычек. Это на самом деле работает несколько лучше для моей первоначальной проблемы с использованием git-log, но я протестировал его в некоторых других контекстах, и это примерно так же непредсказуемо и ненадежно. Что-то странное происходит в кавычках внутри переменных. Я даже не собираюсь публиковать все странные вещи, которые произошли с одинарными кавычками.

Редактировать 2 - Это также не работает: у меня только что была эта замечательная идея, но она не работает вообще:

bash$ mydate="--date=format:%Y-%m-%d\ T%H"
bash$ git log "$mydate"

# Git log output has this:
Date:   2018-04-12\ T23

Таким образом, он не имеет кавычек, заключающих его, но имеет буквенный символ обратной косой черты в строке даты. Кроме того, git log $mydate без ошибок в кавычках с обратным слешем в переменной.

2 ответа2

4

Другой подход:

Когда вы запускаете git log --format="foo bar" , эти кавычки не интерпретируются git - они удаляются оболочкой (и защищают цитируемый текст от разбиения). Это приводит к единственному аргументу:

  • --format=foo bar

Однако, когда переменные без кавычек раскрываются, результаты разделяются на части, а не на кавычки. Таким образом, если ваша переменная содержит --format="foo bar" , она раскрывается в следующие аргументы:

  • --format="foo
  • bar"

Это можно проверить с помощью:

  • переменная printf '% s\n' $

... а также любой простой скрипт, который печатает полученные аргументы.

  • #!Perl /usr /bin /env для $ i (0 ..$ # ARGV) {print ($ i+1). "=".$ ARGV [$ я] "\ п". }
    
  • #!/usr /bin /env python3 импортирует sys для i, arg в перечислении (sys.argv): print (i, "=", arg)
    

Если у вас всегда есть доступный bash, предпочтительным обходным решением является использование переменных массива :

myvar=( --format="foo bar" )

При этом обычный синтаксический анализ выполняется во время присваивания, а не при расширении. Вы используете этот синтаксис для расширения содержимого переменной, каждый элемент получает свой собственный аргумент:

git log "${myvar[@]}"
2

Почему ваша оригинальная команда не работает?

bash$ mydate="--date=format:\"%Y-%m-%d T%H\""
bash$ git log "$mydate"    # This works great.
bash$ git log $mydate
fatal: ambiguous argument 'T%H"': unknown revision or path not in the working tree.

Ты спрашиваешь:

Зачем мне двойные кавычки? Что именно видит git-log после разыменования переменной без двойных кавычек?

Если вы не используете двойные кавычки вокруг $mydate , переменная будет развернута дословно, и перед выполнением строка оболочки будет следующей:

git log --date=format:"%Y-%m-%d T%H"
                      ^————————————^—————— literal quotes

Здесь вы (без необходимости) добавили буквальные кавычки, используя \" в присваивании переменной.

Поскольку команда подвергнется разделению слов, git получит три аргумента, log , --date-format:"%Y-%m%-d и T%H" , поэтому жалуется на то, что не удалось найти ни одного коммита или объекта с именем T%H"


Какой правильный подход?

Если вы хотите сохранить аргументы вместе, если этот аргумент содержит пробелы, вы должны заключить аргумент в кавычки. Как правило, всегда заключайте переменные в двойные кавычки.

Это работает, даже если внутри переменной есть пробел:

mydate="--date=format:%Y-%m-%d T%H"
git log "$mydate"

Теперь третьим аргументом для git будет $mydate , включая пространство, которое вы изначально указали. Все кавычки отбрасываются оболочкой перед передачей в git .

Вам просто не нужны дополнительные кавычки - если все, что вам нужно, это git чтобы увидеть один аргумент, заключите этот аргумент в кавычки при передаче переменной "$mydate" .


Также вы спрашиваете:

bash$ nospace="--date=format:\"%Y-%m-%d\""
bash$ git log $nospace        # Now THIS works great.
bash$ git log "$nospace"      # This kind of works, here is a snippet:

# From git-log output:
Date:   "2018-04-12"

Ваш вопрос:

Фу, почему печатный вывод теперь содержит двойные кавычки?

Потому что вы снова включили в аргумент буквальные кавычки (экранируя их), которые превращаются, скажем, в «реальные» кавычки, когда вы забываете заключить в кавычки переменную в вашей фактической команде. Я говорю «забудь», потому что использование переменных без кавычек в командах оболочки обычно просто доставляет вам неприятности - и здесь оно исправляет ошибку, которую вы допустили, указав переменную в первую очередь.

PS: я знаю, что все это сбивает с толку, но это Bash, и он следует некоторым четким правилам. Здесь нет ошибки. Связанное эссе об именах файлов в shell также очень показательно, так как затрагивает проблему обработки пробелов в Bash.

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .