Есть ли способ сделать локальную команду переносимой для dash ksh bash и zsh?

Внутри Vi (или Vim):

:%s#^05/08/2017#04/03/2017#

Возможно, вам придется сначала нажать Esc , чтобы выйти из режима вставки.

Как и в sed, вы можете выбрать произвольный символ в качестве разделителя шаблона при подстановке с помощью команды s. Здесь я выбрал #.

Шаблон ^05/08/2017будет соответствовать началу строки из-за ^. Это предотвращает случайное совпадение в другом месте любой строки.

С обычным /в качестве разделителя шаблона мы получаем хороший пример того, что называется «синдромом наклонной зубочистки »:

:%s/^05\/08\/2017/04\/03\/2017/

Команда подстановки будет выполняться для всех строк в текущем буфере Vi, так как мы используем %в начале для «диапазона»(%идентично 1,$, т.е. «от первой строки до последней». последняя строка» ).

5
11.09.2019, 20:02
3 ответа

Обратите внимание, что ksh88 и все его клоны выполняют динамическую область видимости и поддерживают localдля него, по крайней мере, с 1990 г. для ksh88 и 1994 г. дляpdksh(и 1989 г. для bash, который (теперь )реализует многое. API ksh88 ).

ksh, о котором вы говорите, — это ksh93, более новая реализация с нуля, разработанная Дэвидом Корном с немного другим и несовместимым API.

Это хорошая идея называть эту оболочку как ksh93, а не просто ksh, поскольку kshсамо по себе означало ksh88API в течение нескольких десятилетий (, который реализуют все, кроме реализации ksh93 ). ksh93не использовался широко до нескольких лет после 2000 года, когда его код был выпущен как открытый исходный код.

ksh93 typesetвыполняет только статическую область видимости. POSIX возражал против указания ksh88 typeset/ localна том основании, что это была динамическая область видимости (, хотя большинство языков, таких как C, делают статическую область видимости, динамическую область видимости. происходит более естественно в оболочках, поскольку это то, что вы получаете с помощью подоболочек или среды ), что объясняет, почему ksh93 был переписан для статического копирования.

Позднее в большинстве других оболочек была реализована область видимости а-ля ksh88, так что ksh93 теперь лишний. И теперь, по иронии судьбы, единственным разумным вариантом POSIX было бы указать динамическую область. В то время как Дэвид Корн изначально отказался реализовать динамическую область видимости в ksh93,он сказал, что может рассмотреть это с ключевым словом/встроенной командой localв списке рассылки POSIX, но он ушел в отставку до того, как это полностью произошло.

Бета-версия ksh93v -и окончательная версия от AT&T могут быть скомпилированы с экспериментальным режимом «bash» (, фактически включенным по умолчанию ), который выполняет динамическую область видимости (в любых формах функций, в том числе с localи typeset), когда ksh93вызывается как bash. Этот режим bashбудет отключен по умолчанию в ksh2020 , хотя псевдонимы local/ declareдля typesetбудут сохранены, даже если режим bash не скомпилирован в(хотя все еще со статической областью видимости ).

Теперь, если мы оставим эту бета-версию в стороне и ее режим bash, ksh93 только выполняет статическую область видимости и только в функциях, объявленных с синтаксисом стиля ksh -(function name { code; }). Вы запутались, потому что функции, объявленные с синтаксисом стиля Bourne -(f() command), вообще не выполняют область видимости . То же самое с исходными файлами или функциями ksh, вызываемыми с помощью . name [args]. В них typesetне объявляется новая переменная в области действия функции (, эта функция не имеет области действия ), она просто обновляет тип переменной в текущей области, которая будет либо глобальной областью или область действия функции стиля ksh -, если этот стиль Bourne -был (в конечном итоге )вызван из функции стиля ksh -.

Код функций в стиле Bourne -запускается так, как будто встроенные/скопированные -вставленные/исходные, где бы они ни вызывались.

var=global
function ksh_function {
  typeset var=private
  echo "ksh1: $var"
  bourne_function
  echo "ksh2: $var"
  other_ksh_function other
  echo "ksh3: $var"
 . other_ksh_function other_invoked_with_dot
  echo "ksh4: $var"
}
bourne_function() {
  typeset var=set-from-bourne-function
}
function other_ksh_function {
  echo "other: $var"
  var=$1
}
ksh_function
echo "global: $var"

Дает:

ksh1: private
ksh2: set-from-bourne-function
other: global
ksh3: set-from-bourne-function
other: set-from-bourne-function
ksh4: other_invoked_with_dot
global: other

В ksh93 невозможно иметь динамическую область видимости, кроме как с помощью подоболочек или самостоятельной реализации стека переменных, как в этом locvarдоказательстве концепции или вы экспортируете переменную чтобы он передавался каждой команде (, включая функции, объявленные с помощью функций стиля ksh -,включая внешние команды через среду ).

В вашем конкретном случае, когда только функция testlocal(, а неtestdescend)нуждается в локальной области, вы можете использовать подход shdef+ kshdef, как описано там или сделать что-то вроде:

case $KSH_VERSION in
  (*" 93"*)
    fn_with_local_scope() {
      alias local=typeset
      eval "function $1 {
        $(cat)
      }"
    }
  ;;
  (*)
    fn_with_local_scope() {
      eval "$1() {
        $(cat)
      }"
    }
  ;;
esac

А затем объявите свои функции как:

fn_with_local_scope testlocal << '}'
  local IFS
  IFS=123
  echo "internal IFS = $IFS"
  testdescend
}

testdescend(){
  echo "descended IFS = $IFS"
}

IFS=abc
testlocal
echo "external IFS = $IFS"

(и используйте localтолько в функциях, объявленных с помощьюfn_with_local_scope).

Что дает

internal IFS = 123
descended IFS = 123
external IFS = abc

во всех оболочках (обратите внимание, что вам нужна последняя версияyash(2.48 или выше )для поддержкиlocal).

Или, если вы согласны, чтобы локальные переменные также экспортировались (только в ksh93):

case $KSH_VERSION in
  (*" 93"*)
    fn() {
      alias local='typeset -x'
      eval "function $1 {
        $(cat)
      }"
    }
  ;;
  (*)
    fn() {
      eval "$1() {
        $(cat)
      }"
    }
  ;;
esac

fn testlocal << '}'
  local IFS
  IFS=123
  echo "internal IFS = $IFS"
  testdescend
}

fn testdescend << '}'
  echo "descended IFS = $IFS"
}

IFS=abc
testlocal
echo "external IFS = $IFS"

Теперь, если бы вы сделали что-то подобное на языке со статической областью видимости, такой как C или ksh93, вы бы сделали что-то вроде:

function testlocal {
  typeset IFS
  IFS=123
  echo "internal IFS = $IFS"
  testdescend "$IFS"
}

function testdescend {
  typeset IFS="$1" # explicitly get the value $IFS from the caller
  echo "descended IFS = $IFS"
}

Мне кажется, что это лучший дизайн, и этот код также будет нормально работать в оболочках с динамической областью видимости (вам все равно нужно будет обратиться к другому синтаксису определения функции ).

Дополнительная литература:

4
27.01.2020, 20:42

Частичное решение

Нет localиспользуется(отсутствует вообще ).

Я нашел не полностью работающее решение, а частичное, которое работает на dash, bash и zsh (по крайней мере ). Просто вызовите функцию с набором переменных окружения:

IFS=123 testlocal

Вы можете протестировать на(Попробуйте онлайн!).

Оболочка ksh (yes ksh93 на используемом сервере )немного сложнее, так как имеет двойную личность. Но обе личности представлены по ссылке выше.

0
27.01.2020, 20:42

Я предпочитаю bash (лучший синтаксис массива IMO ), но иногда сталкиваюсь с ksh-скриптами, поэтому я стараюсь использовать их, когда это удобно:

Всегда используйте синтаксис function func_name { body; }.

Исходя из вышеизложенного, чтобы использовать localключевое слово :перед первой функцией, используйте:

# teach ksh 93 about local
case "$KSH_VERSION" in *' 93'*) alias local='typeset -x' ;; esac

Чтобы получить имя функции внутри функции :, используйте:

local MYNAME="${FUNCNAME[0]:-$0}"
0
27.10.2021, 17:23

Теги

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