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

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

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 
  • Как пропустить первый аргумент в скрипте
  • Универсальная альтернативная альтернатива non-bash `time`?
  • Как запускать команды надстройки по SSH?
  • Какую оболочку я должен изучить для FreeBSD и Debian?
  • Как эхо-строку с несколькими пробелами в bash «нетронутой»?
  • Какой самый простой способ найти неиспользуемый локальный порт?
  • Правильное место для установки командной строки (PS1) при использовании sh / bash / zsh вместе
  • Почему `source foo && true` завершает работу скрипта в bash?
  • разархивировать файл из другой папки
  • разница между «..» и «..», когда я использую ssh usr @ ip 'pscp ...'
  • Список сведений о файлах / каталогах с пробелами
  • Linux и Unix - лучшая ОС в мире.