Установка $0 в исходном коде zsh?

Установка $0 в исходном коде zsh?

Я пытаюсь создать команду, которая ведет себя как «символическая ссылка через ssh», т.е. вызывает удаленный скрипт так, как если бы он был вызван локально. Мой скрипт:

#!/bin/zsh
if (( "$#" = 0 )); then
    echo "Usage: $0 <number>" >&2; exit 1
fi
if ! [[ "$1" =~ '^[0-9]+$' ]]; then
    echo "error: “$1” is not a valid number" >&2; exit 1
fi

Для этого я буду использовать <(command), подстановку процесса, которая создаст fifo с путем вроде /proc/self/fd/<n>, перенаправит commandв него stdout , и выполнит оценку по этому пути. fifo=<(echo 'hi!'); echo $fifo; cat $fifoвыведет echo /proc/self/fd/14(или около того), а затем hi!.

Итак, это должно сработать, давайте посмотрим, сработает ли «Использование». Я сохраню этот скрипт в $PATH и выполню его по имени файла exec-remote.

#!/bin/zsh
source <(ssh myserver 'cat bin/mycommand')

Почти! Это приводит к тому, что удаленный скрипт говорит:

Usage: /proc/self/fd/12 <number>

вместо

Usage: exec-remote <number>

… это означает, что при извлечении кода удаленного скрипта $0устанавливается путь fifo подстановки процесса.

Но команда zsh source, похоже, принимает только позиционные параметры ( $@):

#!/bin/zsh
source <(ssh myserver 'cat bin/mycommand') $0

… заставлю свой сценарий сказать:

error: “/proc/self/fd/12” is not a valid number

Так как же мне заставить zsh выполнить мой удаленный код, не вмешиваясь $0при этом?

решение1

Я думаю, что sourceэто только мешает. Сохраните следующее (локально) как exec-from-myserver:

#!/bin/sh
name=${0##/}
exec zsh -c "$(ssh myserver "cat 'bin/$name'")" "$0" "$@"

Сделайте его исполняемым, а затем создайте на него символическую ссылку:

ln -s exec-from-myserver mycommand
ln -s exec-from-myserver foo-bar-baz
# etc.

(Примечание: строка cat 'bin/whatever_name_you_chose'будетпроанализированона удаленной стороне. Имена с литералом 'сломают код или заставят его вести себя неправильно; вы даже можете выполнить инъекцию кода, назвав символические ссылки определенным образом. Поскольку вы выбираете имена, это не так уж и критично. Используйте обычные имена, и все будет в порядке.)

Теперь, если вы вызовете mycommand, локальный скрипт будет execинтерпретировать zshудаленный bin/mycommandи $0в его контексте будет установлено полезное значение предыдущего (локального) $0. Вызов foo-bar-bazпопытается извлечь и интерпретировать удаленный bin/foo-bar-baz. Примеры:

$ ./mycommand
Usage: ./mycommand <number>
$ # This was your remote script talking.

$ ./mycommand 1
$ # Executed without complaint.

$ ././mycommand
Usage: ././mycommand <number>
$ # As you can see the remote script is aware of its local name and path used.

$ ./foo-bar-baz 
cat: bin/foo-bar-baz: No such file or directory
$ # This was the remote cat talking, only because there is no remote foo-bar-baz.

Примечания:

  • Нюанс в моей терминологии: «удалённый скрипт» существует на удалённой стороне как файл, но интерпретируется локально; «удалённый кот» действительно удалён.
  • exec-from-myserverвсегда использует zsh -c, шебанг в удаленном скрипте полностью игнорируется (ваш эксперимент с sourceтакже проигнорировал его).

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