Я мог бы придумать пару методов, и я думаю, что первый из них менее хакерский для Bash, главным образом потому, что он (мне кажется )имеющим особенности, с которыми легче справиться. Однако, поскольку это также может быть делом вкуса, я рассматриваю и то, и другое.
Способ «до -цитирования»
Он состоит в том, чтобы заставить ваш скрипт расширять свой $@
массив, делая это от имени внутреннего интерактивного bash
, и вы можете использовать /dev/fd/X
файловый -дескриптор -в средстве файловой системы -(, если оно доступно в вашей системе ), в качестве параметра аргумента --init-file
. Такой дескриптор файла -может ссылаться на строку здесь, где вы будете обрабатывать имена файлов, например:
xterm -e bash --init-file /dev/fd/3 3<<<". ~/.bashrc; set -m; vim $@"
Одним из бонусов этого файлового -дескриптора -на -трюке с файловой системой является то, что у вас есть самодостаточное -решение, поскольку оно не зависит от внешних вспомогательных файлов, таких как ваш .vimbashrc
. Это особенно удобно здесь, потому что содержимое --init-file
является динамическим из-за расширения $@
.
С другой стороны, у него есть возможное предостережение относительно фактического сохранения дескриптора файла -от сценария до внутренней оболочки. Этот трюк работает нормально, пока ни один промежуточный процесс не закрывает дескрипторы файла -, которые он получил от своего родителя. Это обычное поведение среди многих стандартных инструментов, но следует, например. a sudo
находится посередине с его поведением по умолчанию, заключающимся в закрытии всех полученных файловых дескрипторов -, тогда этот трюк не сработает, и нам нужно будет прибегнуть к временному файлу или к вашему исходному файлу .vimbashrc
.
Так или иначе,простое использование $@
, как указано выше, не будет работать в случае имен файлов, содержащих пробелы или новые строки, потому что в то время, когда внутренний bash
использует последовательность команд, эти имена файлов не заключаются в кавычки, и, следовательно, пробелы в именах файлов интерпретируются как разделители слов в соответствии с стандартное поведение.
Чтобы решить эту проблему, мы можем внедрить один уровень цитирования, а в версиях Bash 4.4 и выше это всего лишь вопрос использования @Q
синтаксиса преобразования параметров в массив $@
, например:
xterm -e bash --init-file /dev/fd/3 3<<<". ~/.bashrc; set -m; vim ${@@Q}"
В версиях Bash ниже 4.4 мы можем получить то же самое, используя его printf %q
, как этот (как здесь документ для лучшей читабельности, но будет делать то же самое, что и здесь строка, как указано выше):
printf -v files ' %q' "$@"
xterm -e bash --init-file /dev/fd/3 3<<EOF
. ~/.bashrc
set -m
vim $files
EOF
На стороне -примечание: в зависимости от вашей установки вы можете также рассмотреть источник /etc/bash.bashrc
до пользовательского .bashrc
, так как это стандартное поведение Bash для интерактивных оболочек.
Заметьте также, что я оставил команду set -m
для знакомства с вашим исходным сценарием и потому, что она безвредна, но на самом деле здесь она излишня, потому что --init-file
подразумевает интерактивную bash
, которая подразумевает -m
. Вместо этого это было бы необходимо, если, буквально восприняв заголовок вашего вопроса, вы хотели бы, чтобы задание -управляло оболочкой, но не было полностью интерактивным. Есть различия в поведении .
Опция -s
Опция Bash (и POSIX)-s
позволяет указывать аргументы для интерактивной оболочки так же, как вы делаете это для не -интерактивной 1 . Таким образом, при использовании этой опции -s
и при этом в качестве автономного -решения это будет похоже на:
xterm -e bash --init-file /dev/fd/3 -s "$@" 3<<'EOF'
. ~/.bashrc
set -m # superfluous as bash is run with `--init-file`; you would instead need it for a job-controlling yet "non-interactive" bash (ie no `--init-file` nor `-i` option)
exec <<<'exec < /dev/tty; vim "$@"'
EOF
Причудливые вещи, на которые стоит обратить внимание:
$@
внутри здесь-документа будет расширена вашим сценарием (без правильного цитирования и т. д.)вместо внутреннего bash
, где он принадлежит. Это отличается от метода «предварительного -цитирования», когда разделитель документа здесь преднамеренно не -цитируется exec <<<...
Часть перенаправления stdin)должна быть также одинарным -типом в кавычках 2 , иначе часть "$@"
внутри нее будет быть расширен внутренним bash
в то время, когда его массив $@
еще не заполнен exec <<<
Here String )в качестве помощника, просто для того, чтобы внутренний bash
«отложил» выполнение команд, требующих полностью заполненного $@
массив bash
перенаправить свой собственный stdin снова, на этот раз обратно на его управляющий терминал (следовательно, exec < /dev/tty
строка ), чтобы восстановить интерактивную функциональность exec < /dev/tty
(т.е. vim "$@"
здесь )нужно указать в той же строке3 как exec < /dev/tty
, потому что после такого перенаправления Here String больше не будет читаться 4 . На самом деле эта конкретная часть лучше выглядит как Here String, если она может быть достаточно короткой, как в этом примере Этот метод может быть лучше использован с внешним вспомогательным файлом, таким как ваш .vimbashrc
(, хотя удаление собственного -содержало удобство ), поскольку содержимое такого файла, в отношении проблемы аргументов имени файла -, может быть полностью статичным. Таким образом, ваш скрипт, вызываемый менеджером файлов -, станет таким же простым, как:
xterm -e bash --init-file.vimbashrc -s "$@"
и его компаньон .vimbashrc
будут похожи на:
. ~/.bashrc
# (let's do away with the superfluous `set -m`)
exec <<<'exec < /dev/tty && vim "$@"' # let's also run vim only if the redirection to the controlling tty succeeded
Сопутствующий файл по-прежнему имеет свои особенности, но помимо соображений "чистоты",один возможный бонус этой последней версии (non self -содержит )заключается в том, что вся ее команда xterm -e...
, за исключением части "$@"
, может использоваться непосредственно вашим файловым -менеджером на месте вашего «скрипта», , если было бы так любезно разрешить вам указать команду, которую он покорно разбивает на пробелы для создания канонического массива «argv» вместе с аргументами имен файлов.
Также обратите внимание, что весь этот метод -s
во всех его версиях по умолчанию обновляет .bash_history
пользователя с помощью команд, указанных внутри перенаправления вспомогательного стандартного ввода, поэтому я сохраняю эта конкретная часть настолько важна, насколько это возможно. Естественно, вы можете предотвратить такое обновление, используя предпочитаемый вами способ, например. добавив unset HISTFILE
в --init-file
.
--
Просто для сравнения: использование этого метода с dash
было бы намного удобнее, потому что dash
действительно заполняет массив $@
для скрипта, заданного переменной окружения ENV
, и, таким образом, сам -содержащееся решение было бы таким же простым, как:
xterm -e env ENV=/dev/fd/3 dash -s "$@" 3<<'EOF' # `dash` run through `env` just to be positive that `ENV` is present when dash starts
vim "$@"
EOF
ХТХ
1за исключением того, что $0
нельзя указать, в отличие от вызова оболочки с опцией -c
2если вы использовали дополнительный здесь документ, его разделитель также должен быть заключен в кавычки
3на самом деле в той же «полной _команде», что и , определенная POSIX , что означает, что вы все равно можете охватывать несколько строк, если они являются частью одной и той же «полной _команды». ] команда", например когда такие строки имеют обратную косую черту продолжения строки -или находятся внутри составного блока
4это должно быть стандартным поведением, предположительно вытекающим из первого краткого абзаца этого подраздела -раздела
В качестве простого доказательства концепции я попробовал:
. ~/.bashrc
set -m
vim $arg
и:
arg=file-to-edit xterm -e bash --init-file vimbashrc
который ведет себя более или менее так, как вы указали, и позволяет вам передавать аргументы вашей интерактивной команде.
Таким образом, использование временного файла, вероятно, приведет к чему-то вроде
#!/bin/bash
if [ "$1" = "" ] ; then
. ~/.bashrc
set -m
(IFS=$'\n'; vi $(cat "$tmpfile"))
else
tmpfile=$(mktemp /tmp/vimstart.XXXXXX)
for arg in "$@" ; do
echo "$arg" >> $tmpfile
done
tmpfile=$tmpfile xterm -e bash --init-file./vimstart
rm "$tmpfile"
fi
Поскольку имя файла может содержать \n
, вы также можете использовать символ NULL в качестве разделителя:
#!/bin/bash
if [ "$1" = "" ] ; then
. ~/.bashrc
set -m
(IFS=$'\00'; vi $(cat "$tmpfile"))
else
tmpfile=$(mktemp /tmp/vimstart.XXXXXX)
for arg in "$@" ; do
echo -n "$arg" >> $tmpfile
echo -n $'\x00' >> $tmpfile
done
tmpfile=$tmpfile xterm -e bash --init-file./vimstart
rm "$tmpfile"
fi