Почему звездочка в команде приводит к расширению цикла?

Я хочу написать сценарий, который будет запускаться

git diff --name-status master..<BRANCH> 

но когда я запускаю это:

 for i in $(git branch | grep -v master); do echo $i; done 

Я получаю echo один каталог, потому что git branch echo asterisk (у меня есть один каталог в текущем каталоге)

 * <SELECTED BRANCH> 

Почему * расширяется и как я могу предотвратить это расширение?

ОБНОВЛЕНИЕ : я могу предотвратить это, используя это:

 for i in $(git branch | tr -d '*' | grep -v master); done; 

Но почему это происходит? Почему мне нужно удалить звездочку?

2 Solutions collect form web for “Почему звездочка в команде приводит к расширению цикла?”

Необязательные переменные и подстановки команд, такие как $i или $(git …) применяют оператор split + glob к результату строки. То есть:

  1. Создайте строку, содержащую значение переменной или результат команды (минус окончательные строки новой строки в последнем случае).
  2. Разделите строку на отдельные поля в соответствии со значением IFS .
  3. Интерпретируйте каждое поле как шаблон подстановочного знака и замените его на список совпадающих имен файлов; если шаблон не соответствует ни одному файлу, оставьте его неповрежденным.

Выходной сигнал git branch | grep -v master git branch | grep -v master (шаг 1) содержит * master который разделен (шаг 2) на два поля * и master ; * заменяется (шаг 3) на список имен файлов в текущем каталоге.

Вы можете запустить set -f чтобы временно отключить глобальную привязку. Другой подход заключается в том, чтобы избежать подстановки команд и вместо этого проанализировать ввод с read . И все же неважно, но у вас будут ложные имена ветвей, потому что вывод git содержит больше, чем вам нужно (флаг * указывающий текущую ветку, а также такие вещи, как remotes/somewhere/foo -> bar ветви отслеживания). Я думаю, что следующее безопасно, если неэлегантно:

 for i in $(git branch | sed -e 's/^..//' -e 's/ .*//'); do echo $i done 

Я считаю, что надежный способ – использовать git-for-each-ref .

 for i in $(git for-each-ref --format='%(refname:short)' refs/heads refs/remotes); do echo $i done 

Текстовые потоки, подобные этому, следует читать с использованием цикла while, а не for , что, вероятно, вызывает проблему (хотя я не могу воспроизвести ее). Простой способ сделать это:

 git branch | while IFS= read -r line do echo "${line:2}" done 

Сравнение:

 $ cd -- "$(mktemp -d)" $ git init Initialized empty Git repository in /tmp/tmp.MJFmu7q7EH/.git/ $ touch README $ git commit --allow-empty -m "Initial commit" [master (root-commit) da46b03] Initial commit $ git branch foo $ git branch foo * master $ for i in $(git branch) > do > echo $i > done foo * master $ git branch | while IFS= read -r line > do > echo "${line:2}" > done foo master 
  • Как передавать файлы по ssh, а также изменять их разрешения на удаленном сервере без необходимости входа в систему дважды
  • Является ли синтаксис «$ {PS1-}» и чем он отличается от простого «$ PS1»?
  • Использование сгенерированного списка имен файлов в виде списка аргументов - с пробелами
  • Использование AWK для преобразования чисел в 0 или 1
  • Динамически читать имя функции bash изнутри функции bash
  • grep для текста, который может или не может присутствовать
  • .sh-файл, используемый sftp после загрузки и не может быть выполнен
  • Как суммировать значения двух строк в строке в linux
  • Как проверить, какая строка скрипта bash выполняется
  • Удалите все повторяющиеся слова из строки, используя сценарий оболочки
  • Почему расширение оболочки на popd не удаляет каталог из стека?
  • Interesting Posts
    Linux и Unix - лучшая ОС в мире.