Программно открывать новый терминал с помощью команд Bash и запуска, сохраняя управление заданием -

dirname принимает только 1 аргумент; в то время как версия GNU/coreutils dirnameможет принимать более 1 аргумента, это не стандартное расширение -:

SYNOPSIS

    dirname string

0
18.07.2020, 14:29
2 ответа

Я мог бы придумать пару методов, и я думаю, что первый из них менее хакерский для 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

Причудливые вещи, на которые стоит обратить внимание:

  1. Спецификация разделителя здесь-документа должна быть в одинарных кавычках, иначе часть $@внутри здесь-документа будет расширена вашим сценарием (без правильного цитирования и т. д.)вместо внутреннего bash, где он принадлежит. Это отличается от метода «предварительного -цитирования», когда разделитель документа здесь преднамеренно не -цитируется
  2. Строка Here (exec <<<...Часть перенаправления stdin)должна быть также одинарным -типом в кавычках 2 , иначе часть "$@"внутри нее будет быть расширен внутренним bashв то время, когда его массив $@еще не заполнен
  3. в частности, нам нужно такое перенаправление stdin (, сделанное с помощью exec <<<Here String )в качестве помощника, просто для того, чтобы внутренний bash«отложил» выполнение команд, требующих полностью заполненного $@массив
  4. внутри такого вспомогательного перенаправления stdin (части Here String )нам нужно заставить внутренний bashперенаправить свой собственный stdin снова, на этот раз обратно на его управляющий терминал (следовательно, exec < /dev/ttyстрока ), чтобы восстановить интерактивную функциональность
  5. .
  6. нам нужно все команды означает для выполнения после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это должно быть стандартным поведением, предположительно вытекающим из первого краткого абзаца этого подраздела -раздела

2
18.03.2021, 23:19

В качестве простого доказательства концепции я попробовал:

. ~/.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
0
18.03.2021, 23:19

Теги

Похожие вопросы