一致实施tr?

时间:2010-08-25 16:00:18

标签: shell scripting locale

我有一个ksh脚本,使用/dev/urandomtr生成一个长的随机字符串:

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)不包含s​​hell特殊字符。该字符串将在稍后的脚本中作为参数在命令行上使用。我们不想使用像:alnum:这样的类,因为locale可以将这些类转换为在命令行上不起作用的多字节值。在我们进入Solaris之前,这个ksh one-liner完成了大量安装工作。

我们已暂时将此转换为有点令人讨厌的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 个答案:

答案 0 :(得分:2)

您所观察到的操作系统之间没有区别,但是具有不同区域设置的不同计算机。您的Solaris计算机已将LC_COLLATE设置为非默认值,这是您遇到的问题的必然配方。

区域设置是从环境中设置的,如下所示:

  • 如果设置了环境变量LC_ALL,则其值将用于所有类别。

  • 否则,如果设置了LC_FOO,则其值将用于类别LC_FOO

  • 否则,如果设置了LANG,则其值将用于未明确设置的类别。

  • 默认语言环境名为C。在Unix系统上,POSIXC的同义词。

主要的区域设置类别是:

  • LC_CTYPE表示用于文件名,文件内容和终端I / O的字符集和编码。您应该小心保留此设置,除非您知道它不准确(例如,因为特定的文件格式指定了特定的编码)。

  • 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

标准shell实用程序遵循区域设置。 Perl不会,除非你告诉它。

答案 1 :(得分:2)

如果您将路径设置为/ usr / xpg6 / bin /,那么它将按预期工作 这里的语言环境似乎没有任何影响。跨平台黑客是:

tr -dc '[a-z][A-Z][0-9]_-' < /dev/urandom | tr -d '][' | fold -w64 | head -n1

答案 2 :(得分:0)

尝试:

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

还尝试指定tr的完整路径(并将/usr/bin/tr的结果与xpg版本进行比较)。

Solaris上-c(“值”)和-C(“字符”)之间有什么区别?在Linux上他们是一样的。

旁白:您是否可以使用head -c 64来替换fold -w 64 |head -1?此外,您可以取消cattr ... < /dev/urandom | ...

最终,根据可用性,其中一个可能适合您(但字符集可能与您想要的有点不同):

base64 /dev/urandom | head -c 64

uuencode /dev/urandom | head -c 64