#bash #shell #windows-subsystem-for-linux
Вопрос:
Краткие сведения
Использование цикла чтения, который запускает исполняемый файл Windows в сценарии оболочки WSL, приводит к выходу из цикла после первой итерации.
Подробные сведения
Я был совершенно сбит с толку тем, что кажется проблемой совместимости с запуском исполняемых файлов Windows из сценария оболочки в WSL2. Следующий цикл while должен вывести 3 строки, но он будет выводить только «строка 1». Он был протестирован на Ubuntu 20.04 в dash, bash и zsh.
while read -r line; do
powershell.exe /C "echo "${line}""
done << EOF
line 1
line 2
line 3
EOF
Эта проблема также возникает при чтении строк из файла вместо файла heredoc, даже если этот файл имеет окончания строк Windows. Обратите внимание, что если бы powershell был изменен на /bin/bash
или любой другой собственный исполняемый файл, это вывело бы 3 строки. Также powershell можно заменить любым исполняемым файлом Windows cmd.exe, explorer.exe, и т. Д., И он все равно выполнял бы только первую итерацию. Это, по-видимому, проблема именно с чтением, поскольку этот цикл будет работать нормально.
for line in "line 1" "line 2" "line 3"
do
powershell.exe /C "echo "${line}""
done
Обходной путь
Благодаря этому сообщению я обнаружил, что обходной путь заключается в передаче по каналу фиктивной команды: echo "" | cmd.exe /C "echo "${line}""
. Примечание об этом исправлении заключается в том, что, похоже, работает только трубопровод. Перенаправление вывода или запуск его через другой уровень bash не: /bin/bash -c "cmd.exe /C "echo ${line}""
. Я частично публикую это для улучшения видимости для тех, у кого возникнет эта проблема в будущем, но мне все еще любопытно, есть ли у кого-нибудь какое-либо представление о том, почему существует эта проблема (возможно, из-за окончаний строк?). Спасибо!
Комментарии:
1. Возможно , power_shell читает с
stdin
, попробуйте использовать другойfd
в циклеwhile
read
. Отказ от ответственности, я не использую power shell. Вопрос в том, зачем вообще использовать bash? Разве силовая оболочка не может выполнить цикл?2. Аналогичная проблема возникает при использовании
ssh
в сценариях оболочки; вам нужно дать ему-n
возможность запретить чтение stdin.-NonInteractive
Помогает ли опция powershell?3. Нет, все еще ломается. Однако стоит отметить, что проблема возникает даже с программами, которые (не должны?) буду читать дальше
stdin
. Например, изменение powershell наexplorer.exe
откроет только 1 окно вместо 3, как ожидалось.4.Мое объяснение ниже достаточно длинно даже до того, как мы рассмотрим
explorer.exe
пример; -), но вполне вероятно, что WSL все еще вызывает CMD (или какой-либо промежуточный процесс взаимодействия с Windows) при вызове любого.exe
. Поэтому я не удивлен, что неконсольные, не потребляющие stdin команды все еще «поглощают» stdin.5. Спасибо за отличное объяснение! Это казалось таким странным, и мне было очень трудно найти какие-либо подобные проблемы в Интернете, поэтому я надеюсь, что это поможет кому-то еще в будущем 🙂
Ответ №1:
Короткий Ответ:
Немного улучшенное решение echo "" |
состоит в том, чтобы выполнить второе перенаправление с /dev/null
. Это позволяет избежать потенциальных проблем с новой линией echo
, но есть и другие решения:
while read -r line; do
powershell.exe /C "echo "${line}"" < /dev/null
done << EOF
line 1
line 2
line 3
EOF
Объяснение:
Что ж, у вас уже было решение, но на самом деле вы хотели получить объяснение.
Jetchisel и Маркплотник находятся на правильном пути в комментариях. Это, по-видимому, та же основная причина (и решение), что и в этом вопросе ssh
. Чтобы воспроизвести ваш пример с ssh
помощью (при условии ввода ключа, ssh-agent
чтобы не создавалось запроса пароля):
while read -r line; do
ssh hostname echo ${line}
done << EOF
line 1
line 2
line 3
EOF
Вы увидите те же результаты, что и в PowerShell-отображается только «строка 1».
В обоих случаях первая строка переходит к read
оператору, но последующие строки являются stdin, которые используются powershell.exe
(или ssh
) самими собой.
Вы можете увидеть это «доказано» в PowerShell с помощью небольшой модификации вашего сценария:
while read -r line; do
powershell.exe -c "echo "--- ${line} ---"; $input"
done << EOF
line 1
line 2
line 3
EOF
Результаты в:
--- line 1 ---
line 2
line 3
Последующий вопрос, ИМХО, в том, почему bash
нет этой проблемы. Ответ заключается в том, что PowerShell, похоже, всегда использует любой stdin, доступный во время вызова, и добавляет его в переменную $input
magic. С другой стороны, Bash не использует дополнительный stdin до тех пор, пока его явно не попросят:
while read -r line; do
bash -c "echo --- "${line}" ---; cat /dev/stdin"
done << EOF
line 1
line 2
line 3
EOF
Генерирует те же результаты, что и в предыдущем примере PowerShell:
--- line 1 ---
line 2
line 3
В конечном счете, основное решение с помощью PowerShell заключается в том, чтобы принудительно выполнить второе косвенное обращение, которое используется до желаемого ввода. echo "" |
могу это сделать, но будьте осторожны:
while read -r line; do
echo "" | powershell.exe -c "echo "--- ${line} ---"; $input"
done << EOF
line 1
line 2
line 3
EOF
Результаты в:
--- line 1 ---
--- line 2 ---
--- line 3 ---
< /dev/null
у него нет этой проблемы, но вы также могли бы справиться с ней с echo -n "" |
помощью вместо этого.