代码之家  ›  专栏  ›  技术社区  ›  T.Rob

是否一致实施了TR?

  •  3
  • T.Rob  · 技术社区  · 14 年前

    我有一个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: 因为区域设置可以将这些值转换为在命令行中不起作用的多字节值。在我们到达Solaris之前,这个ksh一行程序在许多安装中都完美地完成了这个技巧。

    我们暂时将其转换为一个有点讨厌的PerlRegex。是否有 TR 或者其他一些实用程序或ksh内置的,可以在不同的Unix变体中一致地执行这个任务,并且可以普遍安装?不必是一行,但简单是值得赞赏的。

    更新:我们尝试了没有运气的区域设置。正在等待使用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 回复  |  直到 13 年前
        1
  •  2
  •   pixelbeat    14 年前

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

    tr -dc '[a-z][A-Z][0-9]_-' < /dev/urandom | tr -d '][' | fold -w64 | head -n1
    
        2
  •  2
  •   Gilles 'SO- stop being evil'    14 年前

    您所观察到的操作系统之间并没有什么不同,而是具有不同区域设置的不同机器。您的Solaris机器将lc_collate设置为非默认值,这是解决您遇到的问题的一个可靠方法。

    环境中的区域设置如下:

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

    • 否则,如果 LC_ FOO 已设置,其值用于类别 LCI .

    • 否则,如果 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,否则它不会。

        3
  •  0
  •   Dennis Williamson    14 年前

    尝试:

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

    同时尝试指定 tr (并比较 /usr/bin/tr 到xpg版本)。

    两者有什么区别 -c 在Solaris上使用(“values”)和-c(“characters”)?在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