我有一个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)不包含shell特殊字符。该字符串将在稍后的脚本中作为参数在命令行上使用。我们不想使用像: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上,最后一个版本吐出方括号。
答案 0 :(得分:2)
您所观察到的操作系统之间没有区别,但是具有不同区域设置的不同计算机。您的Solaris计算机已将LC_COLLATE设置为非默认值,这是您遇到的问题的必然配方。
区域设置是从环境中设置的,如下所示:
如果设置了环境变量LC_ALL
,则其值将用于所有类别。
否则,如果设置了LC_FOO
,则其值将用于类别LC_FOO
。
否则,如果设置了LANG
,则其值将用于未明确设置的类别。
默认语言环境名为C
。在Unix系统上,POSIX
是C
的同义词。
主要的区域设置类别是:
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
?此外,您可以取消cat
:tr ... < /dev/urandom | ...
最终,根据可用性,其中一个可能适合您(但字符集可能与您想要的有点不同):
base64 /dev/urandom | head -c 64
或
uuencode /dev/urandom | head -c 64