Создание встроенной -"r" в zsh учитывает только локальную историю в ситуации с общей историей

Простой ответ: :свернуть все разделители в один (первый ).
Для этого требуется цикл (, который выполняется менее log(N)раз):

 var=':a bc::d ef:#$%_+$$%      ^%&*(*&*^
 $#,.::ghi::*::'                           # a long test string.
 d=':@!#$%^&*()_+,.'                       # delimiter set
 f=${d:0:1}                                # first delimiter
 v=${var//["$d"]/"$f"};                    # convert all delimiters to
 :                                         # the first of the delimiter set.
 tmp=$v                                    # temporal variable (v).
 while
     tmp=${tmp//["$f"]["$f"]/"$f"};        # collapse each two delimiters to one
     [[ "$tmp" != "$v" ]];                 # If there was a change
 do
     v=$tmp;                               # actualize the value of the string.
 done

Осталось правильно разделить строку на один разделитель и напечатать:

 readarray -td "$f" arr < <(printf '%s%s' "$v"'' "$f")
 printf '<%s>' "${arr[@]}" ; echo

Нет необходимости ни в set -f, ни в изменении IFS.
Протестировано с пробелами, переводами строк и символами глобуса. Все работает. Довольно медленный (, поскольку следует ожидать, что цикл оболочки будет ).
Но только для bash (bash 4.4+ из-за опции -dдля чтения массива ).


ш

Версия оболочки не может использовать массив, единственным доступным массивом являются позиционные параметры.
Использование tr -s— это всего лишь одна строка (IFS не изменяется в сценарии):

 set -f; IFS=$f command eval set -- '$(echo "$var" | tr -s "$d" "[$f*]" )""'

И распечатать:

 printf '<%s>' "$@" ; echo

Все еще медленно, но не более того.

Команда commandнедействительна в Борне.
в зш,commandвызывает только внешние команды и приводит к сбою eval, если используется command.
В ksh даже с commandзначение IFS изменяется в глобальной области видимости.
И commandприводит к сбою разделения в оболочках, связанных с mksh (mksh, lksh, posh )Удаление команды commandприводит к запуску кода на большем количестве оболочек. Но :удаление commandзаставит IFS сохранить свое значение в большинстве оболочек (eval — это специальная встроенная функция ), за исключением bash (без режима posix )и zsh по умолчанию (без режима эмуляции ). Эта концепция не может работать в zsh по умолчанию ни с command, ни без нее.


Многосимвольная IFS

Да, IFS может быть многосимвольным, но каждый символ будет генерировать один аргумент:

 set -f; IFS="$d" command eval set -- '$(echo "$var" )""'
 printf '<%s>' "$@" ; echo

Будет вывод:

 <><a bc><><d ef><><><><><><><><><      ><><><><><><><><><
 ><><><><><><ghi><><><><><>

В bash вы можете опустить слово command, если оно не используется в эмуляции sh/POSIX. Команда завершится ошибкой в ​​ksh93 (IFS сохранит измененное значение ). В zsh команда commandзаставляет zsh попытаться найти evalкак внешнюю команду (, которую он не находит )и терпит неудачу.

Происходит следующее: единственными символами IFS, которые автоматически сворачиваются в один разделитель, являются пробелы IFS.
Один пробел в IFS свернет все последовательные пробелы в один. Одна вкладка свернет все вкладки. Один пробел и одна табуляция будут сворачивать наборы пробелов и/или табуляции в один разделитель. Повторите идею с новой строкой.

Чтобы свернуть несколько разделителей, нужно немного повозиться.
Предполагая, что ASCII 3 (0x03 )не используется во вводеvar:

 var=${var// /$'\3'}                       # protect spaces
 var=${var//["$d"]/ }                      # convert all delimiters to spaces
 set -f;                                   # avoid expanding globs.
 IFS=" " command eval set -- '""$var""'    # split on spaces.
 set -- "${@//$'\3'/ }"                    # convert spaces back.

Большинство комментариев о ksh, zsh и bash (, о commandи IFS )применимы и здесь.

Значение $'\0'было бы менее вероятным при текстовом вводе, но переменные bash не могут содержать NUL(0x00).

В sh нет внутренних команд для выполнения тех же строковых операций, поэтому tr — единственное решение для сценариев sh.

3
02.02.2021, 14:48
2 ответа

Тот факт, что r -Lне работал должным образом, был признан ошибкой, когда я спросил об этом в списке рассылки zsh-users.

Ошибка была исправлена ​​18 февраля 2021 г. в ветке разработки оболочки Git:

commit 6bef719302d6db33c63fb6f2636986dff1941ac2
Author: Peter Stephenson <retracted>
Date:   Thu Feb 18 21:37:08 2021 +0000

    users/26509: fix for r -L

    fc with the -L option should ignore remote entires, rather than
    reading them and treating them as an error.
0
18.03.2021, 22:33

Вам нужно будет использовать виджет set-local-historyдля этого:

bindkey '^[r' set-local-history

При этом вы можете нажать AltR для переключения между использованием локальной или глобальной истории для таких команд, как rи fc. Если вы не помните, находитесь ли вы в режиме локальной или глобальной истории, нажмите Alt0 + AltR для включения режима глобальной истории или Alt1 + AltR для принудительного режима локальной истории (при условии, что вы используете раскладку клавиатуры по умолчанию emacs).

Почему rc -Lили fc -e - -Lне подходят для вашего случая, я не знаю. Это звучит как ошибка для меня. Вы можете попробовать спросить об этом в списке рассылки Zsh , чтобы узнать, считают ли разработчики это ошибкой или это сделано намеренно.

1
18.03.2021, 22:33

Теги

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