В Bash я запускаю:
alias myalias='echo foo
echo bar
echo baz'
myalias
который возвращает:
foo
bar
baz
Но:
ssh localhost "shopt -s expand_aliases &>/dev/null;
alias myalias='echo foo
echo bar
echo baz'
myalias"
Возврат:
foo
Почему?
решение1
Я думаю, вы нашли ошибку Bash. Эта ошибка специфична для option -c
.
Удаленный запуск не имеет ничего общего с вашей проблемой с многострочным псевдонимом. Вы можете попробовать это в вашем локальном bash. Но не в скрипте bash или интерактивном bash, попробуйте это с -c
опцией, например так
bash -c "shopt -s expand_aliases &>/dev/null;
alias myalias='echo foo
echo bar
echo baz'
myalias"
Тот же вывод, что и у вашей проблемы. Только foo
печатается.
Чтобы получить правильный (ожидаемый) вывод, вам нужно добавить как минимум еще одну строку после myalias
, как предложил @cuonglm.
bash -c "shopt -s expand_aliases &>/dev/null;
alias myalias='echo foo
echo bar
echo baz'
myalias
:"
Почему так происходит? Почему еще одна строка после myalias
помогает?
Я просто хочу сказать, что это не имеет смысла. Ни один документ в Bash не объясняет и не упоминает этот случай, ни капельки. Он не должен работать таким образом. Это ошибка. После прочтения кода вы убедитесь в этом.
Вернитесь к первой проблемной команде. На этот раз ничего не меняйте, просто перекомпилируйте bashс неопределенным "ONESHOT", то вы получите правильный (ожидаемый) вывод. Да, вы не ослышались, команда имеет два разных поведения просто из-за разной конфигурации времени компиляции.
Независимо от того, определяете вы ONESHOT
или нет, это приведет к двум совершенно разным маршрутам в коде Bash для -c "command"
. Если не определять ONESHOT, -c "command"
будет запущен обычный маршрут кода, который является маршрутом кода для почти всех исполнений bash, таких как интерактивные команды и скрипты bash. Но если определить ONESHOT, -c "command"
будет запущен другой конкретный маршрут, который специально разработан только для него, чтобы улучшить его производительность, избегая ветвления.
В этом случае нормальный и наиболее используемый способ может дать правильный вывод, тогда как особый способ не может. Я думаю, что непоследовательное поведение — это не то, чего хотят авторы Bash. Что касается того, какое поведение правильно, я склонен думать, что нормальный способ правильный.
Некоторые подробности об этой ошибке
Следующий фрагмент кода связан с ошибкой. Он взят из функции parse_and_execute() в файле builtins/evalstring.c
while (*(bash_input.location.string))
{
...
}
Этот while
цикл будет выполняться по строкам, обрабатывая одну строку за один цикл. После read myalias
, последней строки, в команде (см. выше), условие в while
станет ложным. myalias
расширяется до трех строк echo, но в этом цикле обрабатывается только одно echo; два других echo будут обработаны в следующем цикле, но... другого цикла нет.
Если вы добавите еще одну строку после myalias
, после read myalias
, условие в while
останется истинным, поэтому два других эха получат шанс запуститься в следующем цикле. Последняя строка после myalias
будет обработана после того, как myalias
будут обработаны все эхи, расширенные на .
ОБНОВЛЯТЬ
Я забыл сказать, какая версия Bash задействована в этой проблеме, а именно
GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)
решение2
Обходной путь (вдохновлен @cuonglm):
ssh localhost "shopt -s expand_aliases &>/dev/null;
alias myalias='ls
echo foo
echo bar
echo baz'
myalias &&
true"
Это сохранит код выхода. true
Однако,долженбыть на новой линии.
Это все еще не объясняет, почему. Но все больше и больше похоже на ошибку.