Bash: Почему псевдоним после новой строки игнорируется при удаленном запуске?

Bash: Почему псевдоним после новой строки игнорируется при удаленном запуске?

В 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Однако,долженбыть на новой линии.

Это все еще не объясняет, почему. Но все больше и больше похоже на ошибку.

Связанный контент