Последовательная реализация tr? - PullRequest
3 голосов
/ 25 августа 2010

У меня есть сценарий ksh, который генерирует длинную случайную строку, используя /dev/urandom и tr:

STRING="$(cat /dev/urandom|tr -dc 'a-zA-Z0-9-_'|fold -w 64 |head -1)"

На серверах Linux и AIX, где я использовал это, получилось 64 символа букв верхнего и нижнего регистра, цифр, тире и символов подчеркивания. Пример:

W-uch3_4fbnk34u2nc08w_nj23n089023ncNjxz979823n23-n88h30pmLCxkMKj

Когда я использовал скрипт в Solaris, диапазоны интерпретировались как литералы, и это приводило к строкам из набора aAzZ09-_. Пример:

AA0z9_aZ-a-z00aZ9_azAZa0zZza9-Az0-_za-9aa0az_a0z-0a0z000-A9Z_0a

Как ни странно, на этом сервере Solaris страница руководства для tr указывает, что используемый синтаксис должен был дать желаемый результат.

Идея состоит в том, чтобы использовать /dev/urandom для создания псевдослучайной строки, из которой мы извлекаем символы, так что результат a) не содержит пробелов и b) не содержит специальных символов оболочки. Строка будет использоваться в командной строке в качестве аргумента позже в скрипте. Мы не хотим использовать классы типа :alnum:, потому что локаль может преобразовать их в многобайтовые значения, которые не работают в командной строке. Этот однострочный ksh отлично справлялся со многими установками, пока мы не добрались до Solaris.

Мы временно превратили это в довольно неприятное регулярное выражение Perl. Существует ли синтаксис для tr или какой-либо другой встроенной утилиты или ksh, которая будет выполнять эту задачу последовательно во всех вариантах UNIX и универсально установлена? Не должен быть однострочным, но простота ценится.

Обновление: мы попробовали настройки Locale без удачи. Ожидание результатов использования версии xpg6.

$ uname -a
SunOS hostname 5.10 Generic_142900-04 sun4u sparc SUNW,SPARC-Enterprise
$ cat /dev/urandom | tr -dc "a-zA-Z0-9-_" | fold -w 64 | head -1 | sed 's/^-/_/'
0-a9-z9a_zzZAa_a_0az-9_z0a_90Z_9az09aZzZAa-9aa_-__za0ZA9_ZzzZazA
$ set | grep '^L[AC]'
LANG=C
LC_ALL=C
LC_COLLATE=en_US
LC_CTYPE=en_US
LC_MESSAGES=en_US
LC_MONETARY=en_US
LC_NUMERIC=en_US
LC_TIME=en_US
$ export LC_CTYPE="$LC_ALL" LC_MESSAGES="$LC_ALL"
$ set | grep '^L[AC]'
LANG=C
LC_ALL=C
LC_COLLATE=en_US
LC_CTYPE=C
LC_MESSAGES=C
LC_MONETARY=en_US
LC_NUMERIC=en_US
LC_TIME=en_US
$ cat /dev/urandom | tr -dc "a-zA-Z0-9-_" | fold -w 64 | head -1 | sed 's/^-/_/'
0900z9az99_a0za09__0zA0_Z--Z_-Aa-AaA9zAZz-Aa90A00z__ZzA9A-Z0aA_-
$ unset LC_ALL; export LC_COLLATE=C LC_NUMERIC=C LC_TIME=C
$ set | grep '^L[AC]'
LANG=C
LC_COLLATE=C
LC_CTYPE=C
LC_MESSAGES=C
LC_MONETARY=en_US
LC_NUMERIC=C
LC_TIME=C
$ cat /dev/urandom | tr -dc "a-zA-Z0-9-_" | fold -w 64 | head -1 | sed 's/^-/_/'
_AA9aA_Za-A0-AZa_A-0ZA--a_za-a9zZZz__a0az_-0A-9-0aA-0za00A-__9-0
$ unset LANG LC_COLLATE LC_NUMERIC LC_TIME
$ set | grep '^L[AC]'
LC_CTYPE=C
LC_MESSAGES=C
LC_MONETARY=en_US
$ cat /dev/urandom | tr -dc "a-zA-Z0-9-_" | fold -w 64 | head -1 | sed 's/^-/_/'
_-_9zz9Z-Z-Z-Z_0_a9zzzZZaAa--9_zAZaaAZz-ZaAZ09Z-_z-zz09ZZAzAz0Z0
$ unset LC_CTYPE LC_MESSAGES LC_MONETARY
$ set | grep '^L[AC]'
$ cat /dev/urandom | tr -dc "a-zA-Z0-9-_" | fold -w 64 | head -1 | sed 's/^-/_/'
_0aAa9_Z_a_Z--_Az-aa0ZA0ZzZ-9Aa9-Z0--0A_Z0Zaz-AA_Zz0z---Z_99z_a9
$ export LANG=C LC_ALL=C LC_COLLATE=C LC_CTYPE=C LC_MESSAGES=C LC_MONETARY=C LC_NUMERIC=C LC_TIME=C
$ set | grep '^L[AC]'
LANG=C
LC_ALL=C
LC_COLLATE=C
LC_CTYPE=C
LC_MESSAGES=C
LC_MONETARY=C
LC_NUMERIC=C
LC_TIME=C
$ cat /dev/urandom | tr -dc "a-zA-Z0-9-_" | fold -w 64 | head -1 | sed 's/^-/_/'
Za_000z9aa--aA00zAAZza0AA90090--z0a00_zZ9ZA0_---aZZ09a0ZA0_0zZaa
$ cat /dev/urandom | tr -dc "[a-z][A-Z][0-9]-_" | fold -w 64 | head -1 | sed 's/^-/_/'
x7dni9gIXVF6AHQc3B-H6hjnBVHChJ9zM-z5EQ5UEruATI_NNFaCoVLOqM6gVaT5
$

Конечно, в Linux эта последняя версия выплевывает квадратные скобки.

Ответы [ 3 ]

2 голосов
/ 25 августа 2010

То, что вы заметили, не отличается между операционными системами, но разные машины имеют разные настройки локали. Для вашей машины Solaris значение LC_COLLATE не равно значению по умолчанию, что является верным рецептом для решения проблем, которые у вас есть.

Настройки локали устанавливаются из среды следующим образом:

  • Если установлена ​​переменная окружения LC_ALL, ее значение используется для всех категорий.

  • В противном случае, если установлен LC_<em>FOO</em>, его значение используется для категории LC_<em>FOO</em>.

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

  • Язык по умолчанию называется C. В системах Unix POSIX является синонимом C.

Основные категории локалей:

  • LC_CTYPE указывает набор символов и кодировку, используемые для имен файлов, содержимого файлов и терминального ввода-вывода. Вам следует тщательно сохранить этот параметр, если только вы не знаете, что он неточный (например, потому что определенный формат файла определяет конкретную кодировку).

  • LC_MESSAGES - это язык сообщений, которые видит пользователь. Вы должны сохранить эту настройку. Если вам действительно нужно разобрать сообщение об ошибке, установите LC_MESSAGES=C.

  • LC_COLLATE указывает порядок сортировки символов. Это почти всегда нежелательно в сценариях. Большинство значений, отличных от C, вызывают проблемы, такие как A - Z, соответствующие строчным буквам.

  • Иногда LC_NUMERIC может вызывать проблемы, поскольку числа могут быть напечатаны с различной пунктуацией, а LC_TIME влияет на то, как некоторые команды показывают дату и время. Другие категории почти никогда не имеют значения в сценариях.

Вот разумная стратегия для скриптов (предупреждение, набираемое прямо в браузере):

unset LANGUAGE  # a GNU-specific setting
if [ -n "$LC_ALL" ]; then
  export LC_CTYPE="$LC_ALL" LC_MESSAGES="$LC_ALL"
  unset LC_ALL
elif [ -n "$LANG" ]; then
  export LC_COLLATE=C LC_NUMERIC=C LC_TIME=C
else
  unset LC_COLLATE LC_NUMERIC LC_TIME
fi

Стандартные утилиты оболочки подчиняются настройкам локали. Perl не делает, если вы не скажете это.

2 голосов
/ 26 августа 2010

Если вы установите путь к / usr / xpg6 / bin /, то он будет работать как положено. Локаль, похоже, здесь не влияет.Кроссплатформенный хак это:

tr -dc '[a-z][A-Z][0-9]_-' < /dev/urandom | tr -d '][' | fold -w64 | head -n1
0 голосов
/ 25 августа 2010

Попробуйте:

LANG=C tr -dc 'a-zA-Z0-9-_'

также попробуйте указать полный путь к tr (и сравните результаты /usr/bin/tr с версией xpg).

В чем разница между -c ("значения") и -C ("символы") в Solaris? В Linux они одинаковы.

В сторону: Вы можете использовать head -c 64 вместо fold -w 64 |head -1? Также вы можете исключить cat: tr ... < /dev/urandom | ...

В конечном итоге, в зависимости от доступности, один из них может работать для вас (но набор символов может немного отличаться от того, что вы хотите):

base64 /dev/urandom | head -c 64

или

uuencode /dev/urandom | head -c 64
...