Это возможно, но безобразно:
for action in status start stop restart; do
eval "$action() { systemctl $action \"\$@\"; }"
done
Как и в случае с чем-либо, связанным с eval
, это сложно сделать правильно.eval
выполняет синтаксический анализ команды дважды и выполняет ее при втором анализе.«А?»Я слышал, вы говорите?Дело в том, что обычно $variable
ссылки в определении функции раскрываются не сразу, а при выполнении функции.Поэтому, когда ваш цикл запускает это (с action
установленным в «состояние»):
$action() {
systemctl $action $*
done
Расширяет первую ссылку до $action
, но не вторую, давая это:
status() {
systemctl $action $*
done
Вместо этого вы хотите, чтобы обе ссылки на $action
были немедленно расширены.Но вы не хотите, чтобы ссылка на $*
была немедленно расширена, потому что тогда он будет использовать аргументы вашего скрипта, а не аргументы, переданные функции во время выполнения.И на самом деле, вы вообще не хотите $*
, потому что это искажает аргументы при некоторых обстоятельствах;вместо этого используйте "$@"
.
Таким образом, вам нужен способ мгновенного расширения ссылок на переменные / параметры и откладывания некоторых на потом.eval
дает вам это.Самое сложное в том, что вам могут потребоваться два уровня цитирования / экранирования (один для первого прохода анализа, один для второго), и вам нужно использовать эти уровни для управления тем, какие ссылки на переменные / параметры раскрываются немедленно, а какие позже.
Когда это выполняется (с action
, установленным в «status»):
eval "$action() { systemctl $action \"\$@\"; }"
... он выполняет парсинг, расширяет ссылки на неэкранированные переменные и удаляет уровень цитированияи убегая, давая это:
status() { systemctl status "$@"; }
... что вы и хотели.