функция urlencode

Каждый раз, когда необходимо получить доступ к ssh серверу на порте не по умолчанию или с другим именем пользователя, можно определить псевдоним в ~/.ssh/config.

Host mysvn
HostName server.com
Port 20000
User jm

Затем выполненный svn co svn+ssh://mysvn/home/svn/proj1.

4
17.11.2018, 22:46
1 ответ

[TL, DR: используйте urlencode_grouped_case версия в последнем блоке кода.]

Awk может сделать большую часть задания, за исключением того, что он раздражающе испытывает недостаток в способе преобразовать от символа до его числа. Если od присутствует на Вашем устройстве, можно использовать его для преобразования всех символов (более точно, байты) в соответствующее число (записанный в десятичном числе, так, чтобы awk мог считать его), затем используйте awk для преобразования допустимых символов назад в литералы и заключенные в кавычки символы в надлежащую форму.

urlencode_od_awk () {
  echo "$1" | od -t d1 | awk '{
      for (i = 2; i <= NF; i++) {
        printf(($i>=48 && $i<=57) || ($i>=65 &&$i<=90) || ($i>=97 && $i<=122) ||
                $i==45 || $i==46 || $i==95 || $i==126 ?
               "%c" : "%%%02x", $i)
      }
    }'
}

Если Ваше устройство не имеет od, можно сделать все в оболочке; это значительно поможет производительности (меньше вызовов к внешней программе — ни один, если printf встроенное) и быть легче записать правильно. Я полагаю, что все оболочки Busybox поддерживают ${VAR#PREFIX} создайте для обрезки префикса от строки; используйте его для лишения первого символа строки неоднократно.

urlencode_many_printf () {
  string=$1
  while [ -n "$string" ]; do
    tail=${string#?}
    head=${string%$tail}
    case $head in
      [-._~0-9A-Za-z]) printf %c "$head";;
      *) printf %%%02x "'$head"
    esac
    string=$tail
  done
  echo
}

Если printf не встроенное, а внешняя утилита, Вы снова получите производительность путем вызова ее только однажды для целой функции вместо однажды на символ. Создайте формат и параметры, затем выполните единственный вызов к printf.

urlencode_single_printf () {
  string=$1; format=; set --
  while [ -n "$string" ]; do
    tail=${string#?}
    head=${string%$tail}
    case $head in
      [-._~0-9A-Za-z]) format=$format%c; set -- "$@" "$head";;
      *) format=$format%%%02x; set -- "$@" "'$head";;
    esac
    string=$tail
  done
  printf "$format\\n" "$@"
}

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

urlencode_grouped_literals () {
  string=$1; format=; set --
  while
    literal=${string%%[!-._~0-9A-Za-z]*}
    if [ -n "$literal" ]; then
      format=$format%s
      set -- "$@" "$literal"
      string=${string#$literal}
    fi
    [ -n "$string" ]
  do
    tail=${string#?}
    head=${string%$tail}
    format=$format%%%02x
    set -- "$@" "'$head"
    string=$tail
  done
  printf "$format\\n" "$@"
}

В зависимости от параметров компиляции, [ (иначе. test) может быть внешняя утилита. Мы только используем его для сопоставления строк, которое может также быть сделано в оболочке с case создать. Вот последние два подхода, переписанные для предотвращения test встроенный, первый идущий символ символом:

urlencode_single_fork () {
  string=$1; format=; set --
  while case "$string" in "") false;; esac do
    tail=${string#?}
    head=${string%$tail}
    case $head in
      [-._~0-9A-Za-z]) format=$format%c; set -- "$@" "$head";;
      *) format=$format%%%02x; set -- "$@" "'$head";;
    esac
    string=$tail
  done
  printf "$format\\n" "$@"
}

и копируя каждый литеральный сегмент в пакете:

urlencode_grouped_case () {
  string=$1; format=; set --
  while
    literal=${string%%[!-._~0-9A-Za-z]*}
    case "$literal" in
      ?*)
        format=$format%s
        set -- "$@" "$literal"
        string=${string#$literal};;
    esac
    case "$string" in
      "") false;;
    esac
  do
    tail=${string#?}
    head=${string%$tail}
    format=$format%%%02x
    set -- "$@" "'$head"
    string=$tail
  done
  printf "$format\\n" "$@"
}

Я протестировал на своем маршрутизаторе (процессор MIPS, DD-WRT-based распределение, BusyBox с пеплом, внешним printf и [). Каждая версия является значимым улучшением скорости на предыдущем. Перемещение в единственное ветвление является старшим значащим улучшением; это - то, которое заставляет функцию ответить почти немедленно (в человеческих терминах) в противоположность после нескольких секунд для реалистического длинного параметра URL.

6
27.01.2020, 20:52
  • 1
    Хороший ответ. Я добавлю, что busybox (и другие подобные вещи) мог заменить обычно встроенные команды (printf, даже отозваться эхом!) с вызовом самого busybox (я видел это поведение в версии mobaXterm 3's удара busybox, например): создание некоторого сценария surprinsingly медленный (простое for i in ... ; do echo i ; done цикл в busybox версии 'удара' вызовет busybox n времена, 1 на эхо, тогда как в ударе эхо было бы встроено). Попытайтесь действительно трудно поместить только 1 вызов в любую подкоманду, чтобы не иметь несколько вызов busybox (каждый с большими издержками). set -x помогает найти их. –  Olivier Dulac 09.01.2013, 10:03
  • 2
    @OlivierDulac прокси Ли ветвления пепла BusyBox на builtins как echo зависит от параметра компиляции (ENABLE_FEATURE_SH_NOFORK), который я думаю, делает его быстрее, но багги в угловых случаях (прерывания, эффекты специального builtins). попытка –  Gilles 'SO- stop being evil' 09.01.2013, 12:58
  • 3
    благодарит, хороший для знания (я не могу действительно перекомпилировать тот в mobaXterm, и я предпочитаю, чтобы они выбрали самый совместимый approcah, но хороший для знания так или иначе) –  Olivier Dulac 09.01.2013, 14:11

Теги

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