Friday, 20 April 2018

从 ls 排序问题开始探索


网上有人问为什么ls ascii 排序, > 在前, - 在后, 这不科学啊。

首先, type -a ls 知道 ls 完整路径是 /bin/ls,然后(假设系统是 debian-based)  dpkg -S /bin/ls 知道 ls 属于 coreutils package。最后 apt source coreutils  下载源代码。

f
下载完毕后,find . -name '*ls*.c' 查找到 ls.c 路径, 然后看见:

/* Read directory NAME, and list the files in it.
   If REALNAME is nonzero, print its name instead of NAME;
   this is used for symbolic links to directories.
   COMMAND_LINE_ARG means this directory was mentioned on the command line.  */

static void
print_dir (char const *name, char const *realname, bool command_line_arg)
{
  DIR *dirp;
  struct dirent *next;
  uintmax_t total_blocks = 0;
  static bool first = true;

  errno = 0;
  dirp = opendir (name);
  if (!dirp)
    {
      file_failure (command_line_arg, _("cannot open directory %s"), name);
      return;
    }

  if (LOOP_DETECT)
    {
      struct stat dir_stat;
      int fd = dirfd (dirp);

      /* If dirfd failed, endure the overhead of using stat.  */
      if ((0 <= fd
           ? fstat (fd, &dir_stat)
           : stat (name, &dir_stat)) < 0)

可以看见 opendir  函数拿到目录 pointer dirp  后,把 pointer  传入 dirfd 函数获得目录 fd ... 省略。  再往后看见  dirp 传入 readdir 循环获取目录全部文件:

/* Read the directorCy entries, and insert the subfiles into the 'cwd_file'
     table.  */

  while (1)
    {
      /* Set errno to zero so we can distinguish between a readdir failure
         and when readdir simply finds that there are no more entries.  */
      errno = 0;
      next = readdir (dirp);

简单的程序来模拟 :

/*
 * This program displays the names of all files in the current directory.
 */

#include <dirent.h>
#include <stdio.h>
#include <locale.h>

int main(void) {
  DIR *d;
  struct dirent *dir;
    setlocale (LC_ALL, "");

  d = opendir(".");
  if (d) {
    while ((dir = readdir(d)) != NULL) {
      printf("%s\n", dir->d_name);
    }
    closedir(d);
  }
  return(0);
}

编译运行后可以发现文件是没有意义的随机。网上有人如此解释:

    The entries are probably returned in whatever order the implementor figured would be the fasted order to return them.

    Traditionally Unix filesystems store a list files and directories in an unsorted list.  Think of it as an array.  The fasted way to return the items is to just loop over the array.

    A fast way to insert an item into the array is to insert in the next unused slot.  Suppose the filesystem does not keep an index that points to the next free slot so the system just loops over the array until it finds a free slot.

    A fast way to remove an item is to loop over the array to find the item and then just mark that slot as unused.  The system could sort the array when an item is removed but that takes time so it likely just leaves an open slot wherever an item is removed.

    There are filesystems (like ReiserFS) that use trees for indexing to give faster searching.  In the end the order that items are returned by readdir is not defined to be sorted in any particular way so it's up to the application to sort the items as required.


可以总结: 从系统获得文件名时是并非默认排序好的,而是 ls 程序较后做了 sort 的动作。

ls.c 默认排序是名字 sort_name:

/* The file characteristic to sort by.  Controlled by -t, -S, -U, -X, -v.
   The values of each item of this enum are important since they are
   used as indices in the sort functions array (see sort_files()).  */

enum sort_type
  {
    sort_none = -1,     /* -U */
    sort_name,          /* default */
    sort_extension,     /* -X */
    sort_size,          /* -S */
    sort_version,       /* -v */
    sort_time,          /* -t */
    sort_numtypes       /* the number of elements of this enum */
  };


ls.c 的 sort_files 会把 sort_functions 比较函数传入 mpsort 函数:

/* Sort the files now in the table.  */

static void
sort_files (void)
{
  bool use_strcmp;

  if (sorted_file_alloc < cwd_n_used + cwd_n_used / 2)
    {
      free (sorted_file);
      sorted_file = xnmalloc (cwd_n_used, 3 * sizeof *sorted_file);
      sorted_file_alloc = 3 * cwd_n_used;
    }

  initialize_ordering_vector ();

  if (sort_type == sort_none)
    return;

  /* Try strcoll.  If it fails, fall back on strcmp.  We can't safely
     ignore strcoll failures, as a failing strcoll might be a
     comparison function that is not a total order, and if we ignored
     the failure this might cause qsort to dump core.  */

  if (! setjmp (failed_strcoll))
    use_strcmp = false;      /* strcoll() succeeded */
  else
    {
      use_strcmp = true;
      assert (sort_type != sort_version);
      initialize_ordering_vector ();
    }

  /* When sort_type == sort_time, use time_type as subindex.  */
  mpsort ((void const **) sorted_file, cwd_n_used,
          sort_functions[sort_type + (sort_type == sort_time ? time_type : 0)]
                        [use_strcmp][sort_reverse]
                        [directories_first]);

sort_function 的定义包括了 xstrcoll 和 strcmp 两大比较函数(以及 rev 版本), 用哪一个取决于上面的 use_strcmp :

/* Define the 8 different sort function variants required for each sortkey.
   KEY_NAME is a token describing the sort key, e.g., ctime, atime, size.
   KEY_CMP_FUNC is a function to compare records based on that key, e.g.,
   ctime_cmp, atime_cmp, size_cmp.  Append KEY_NAME to the string,
   '[rev_][x]str{cmp|coll}[_df]_', to create each function name.  */
#define DEFINE_SORT_FUNCTIONS(key_name, key_cmp_func)           \
  /* direct, non-dirfirst versions */                   \
  static int xstrcoll_##key_name (V a, V b)             \
  { return key_cmp_func (a, b, xstrcoll); }             \
  static int strcmp_##key_name (V a, V b)               \
  { return key_cmp_func (a, b, strcmp); }               \


再看 xstrcoll 是神马:

/* Use strcoll to compare strings in this locale.  If an error occurs,
   report an error and longjmp to failed_strcoll.  */

static jmp_buf failed_strcoll;

static int
xstrcoll (char const *a, char const *b)
{
  int diff;
  errno = 0;
  diff = strcoll (a, b);
  if (errno)
    {
      error (0, errno, _("cannot compare file names %s and %s"),
             quote_n (0, a), quote_n (1, b));
      set_exit_status (false);
      longjmp (failed_strcoll, 1);
    }
  return diff;
}



已经非常清楚是调用了 strcoll 这个 C 标准库函数 ("coll" 顾名思义联想到 LC_COLLATE)。 而注释 Use strcoll to compare strings in this locale. 也解释了 strcoll 依赖于 locale。


口说无凭,让我们调用代码:

#include <stddef.h>
#include <locale.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void test(char * a, char * b);

int main(int argc, const char * argv[])
{
    test("a", "c");
    test("1", "3");
    test("1", "11");
    test("a", "A");
    test("-", ">");
    test("+", "a");
    test("+", "{");
    return 0;
}

void test(char * a, char * b) {
    int result;
    setlocale (LC_ALL, ""); //环境变量 LC_ALL 一旦设空,即会使用单独的 LC_COLLATE
    char str1[140];
    char str2[140];
    strcpy(str1, a);
    strcpy(str2, b);
    result = strcoll(str1, str2);
    if (result == 0) printf("Strings are the same\n");
    if (result > 0) printf("%s is greater than the %s\n", a, b);
    else printf("%s is less than the %s\n", a, b);
}

编译后输出结果, 证明确实是 strcoll 被 locale 影响:

xb@dnxb:/tmp$ ./a.out
a is less than the c
1 is less than the 3
1 is less than the 11
a is less than the A
- is greater than the >
+ is less than the a
+ is greater than the {
xb@dnxb:/tmp$ LC_COLLATE=C ./a.out
a is less than the c
1 is less than the 3
1 is less than the 11
a is greater than the A
- is less than the >
+ is less than the a
+ is less than the {
xb@dnxb:/tmp$


因此只需要更改 locale 环境变量,即可影响 ls 的输出:

xb@dnxb:/tmp/test/sub$ locale |grep -E 'LANG|COLL' #当前系统 locale
LANG=en_US.UTF-8
LANGUAGE=
LC_COLLATE="en_US.UTF-8"
xb@dnxb:/tmp/test/sub$ ls
`  ×  <  =  >  -  ,  ;  :  !  ?  ¿  "  «  ]  {  \  #  1  2  a  b  m
xb@dnxb:/tmp/test/sub$ LC_COLLATE=en_US.UTF-8 ls
`  ×  <  =  >  -  ,  ;  :  !  ?  ¿  "  «  ]  {  \  #  1  2  a  b  m
xb@dnxb:/tmp/test/sub$ LC_COLLATE=C ls
!  "  #  ,  -  1  2  :  ;  <  =  >  ?  \  ]  `  a  b  m  {  «  ¿  ×
xb@dnxb:/tmp/test/sub$ LC_COLLATE=C.UTF-8 ls
!  "  #  ,  -  1  2  :  ;  <  =  >  ?  \  ]  `  a  b  m  {  «  ¿  ×
xb@dnxb:/tmp/test/sub$


可以看出 LC_COLLATE=C 就是你想要的纯 ascii 代码排序:

xb@dnxb:/tmp/test/sub$ LC_COLLATE=C ls | hexdump -C
00000000  21 0a 22 0a 23 0a 2c 0a  2d 0a 31 0a 32 0a 3a 0a  |!.".#.,.-.1.2.:.|
00000010  3b 0a 3c 0a 3d 0a 3e 0a  3f 0a 5c 0a 5d 0a 60 0a  |;.<.=.>.?.\.].`.|
00000020  61 0a 62 0a 6d 0a 7b 0a  c2 ab 0a c2 bf 0a c3 97  |a.b.m.{.........|
00000030  0a                                                |.|
00000031
xb@dnxb:/tmp/test/sub$


至于 LC_COLLATE=C.UTF-8 的好处是支持中文输出, 而不是 ? 乱码。

继续深入:

xb@dnxb:/tmp$ localedef --help | tail -n 5
System's directory for character maps : /usr/share/i18n/charmaps
                       repertoire maps: /usr/share/i18n/repertoiremaps
                       locale path    : /usr/lib/locale:/usr/share/i18n
For bug reporting instructions, please see:
... 和谐链接
xb@dnxb:/tmp$ ls /usr/share/i18n
charmaps  locales  SUPPORTED
xb@dnxb:/tmp$ ls /usr/lib/locale
C.UTF-8  locale-archive
xb@dnxb:/tmp$


看到 locale 相关的目录 i18n, 搜索 LC_COLLATE 能发现 "iso14651_t1" 关键字 。

xb@dnxb:/tmp$ grep 'END LC_COLL' /usr/share/i18n/locales/en_US  -B 5
LC_COLLATE

% Copy the template from ISO/IEC 14651
copy "iso14651_t1"

END LC_COLLATE
xb@dnxb:/tmp$


随便网搜到 ISO 14651 的维基百科就知道要去看 iso 文档,我穷小子没钱买只能下载。

边看 iso 文档了解, 继续搜:

xb@dnxb:/tmp$ find  /usr/share/i18n/ -name '*iso14651*'
/usr/share/i18n/locales/iso14651_t1_pinyin
/usr/share/i18n/locales/iso14651_t1_common
/usr/share/i18n/locales/iso14651_t1
xb@dnxb:/tmp$


打开 iso14651_t1_common 文件搜索 } 就能看见梦寐以求的 table 啦:

 order_start <SPECIAL>;forward;backward;forward;forward,position
#
# Tout caractère non précisément défini sera considéré comme caractère spécial
# et considéré uniquement au dernier niveau.
#
# Any character not precisely specified will be considered as a special
# character and considered only at the last level.
# <U0000>......<U7FFFFFFF> IGNORE;IGNORE;IGNORE;<U0000>......<U7FFFFFFF>
#
# SYMB.                                N° GLY
#
<U0020> IGNORE;IGNORE;IGNORE;<U0020> # 32 <SP>
<U005F> IGNORE;IGNORE;IGNORE;<U005F> # 33 _
<U0332> IGNORE;IGNORE;IGNORE;<U0332> # 34 <"_>
<U00AF> IGNORE;IGNORE;IGNORE;<U00AF> # 35 - (MACRON)
<U00AD> IGNORE;IGNORE;IGNORE;<U00AD> # 36 <SHY>
<U002D> IGNORE;IGNORE;IGNORE;<U002D> # 37 -
<U002C> IGNORE;IGNORE;IGNORE;<U002C> # 38 ,
<U003B> IGNORE;IGNORE;IGNORE;<U003B> # 39 ;
<U003A> IGNORE;IGNORE;IGNORE;<U003A> # 40 :
<U0021> IGNORE;IGNORE;IGNORE;<U0021> # 41 !
<U00A1> IGNORE;IGNORE;IGNORE;<U00A1> # 42 ¡
<U003F> IGNORE;IGNORE;IGNORE;<U003F> # 43 ?
<U00BF> IGNORE;IGNORE;IGNORE;<U00BF> # 44 ¿
<U002F> IGNORE;IGNORE;IGNORE;<U002F> # 45 /
<U0338> IGNORE;IGNORE;IGNORE;<U0338> # 46 <"/>
<U002E> IGNORE;IGNORE;IGNORE;<U002E> # 47 .
<U00B7> IGNORE;IGNORE;IGNORE;<U00B7> # 58 ×
<U00B8> IGNORE;IGNORE;IGNORE;<U00B8> # 59 ¸
<U0328> IGNORE;IGNORE;IGNORE;<U0328> # 60 <";>
<U0027> IGNORE;IGNORE;IGNORE;<U0027> # 61 '
<U2018> IGNORE;IGNORE;IGNORE;<U2018> # 62 <'6>
<U2019> IGNORE;IGNORE;IGNORE;<U2019> # 63 <'9>
<U0022> IGNORE;IGNORE;IGNORE;<U0022> # 64 "
<U201C> IGNORE;IGNORE;IGNORE;<U201C> # 65 <"6>
<U201D> IGNORE;IGNORE;IGNORE;<U201D> # 66 <"9>
<U00AB> IGNORE;IGNORE;IGNORE;<U00AB> # 67 «
<U00BB> IGNORE;IGNORE;IGNORE;<U00BB> # 68 »
<U0028> IGNORE;IGNORE;IGNORE;<U0028> # 69 (
<U207D> IGNORE;IGNORE;IGNORE;<U207d> # 70 <(S>
<U0029> IGNORE;IGNORE;IGNORE;<U0029> # 71 )
<U207E> IGNORE;IGNORE;IGNORE;<U207E> # 72 <)S>
<U005B> IGNORE;IGNORE;IGNORE;<U005B> # 73 [
<U005D> IGNORE;IGNORE;IGNORE;<U005D> # 74 ]
<U007B> IGNORE;IGNORE;IGNORE;<U007B> # 75 {
<U007D> IGNORE;IGNORE;IGNORE;<U007D> # 76 }
<U00A7> IGNORE;IGNORE;IGNORE;<U00A7> # 77 §
<U00B6> IGNORE;IGNORE;IGNORE;<U00B6> # 78 ¶
<U00A9> IGNORE;IGNORE;IGNORE;<U00A9> # 79 ©
<U00AE> IGNORE;IGNORE;IGNORE;<U00AE> # 80 ®
<U2122> IGNORE;IGNORE;IGNORE;<U2122> # 81 <TM>
<U0040> IGNORE;IGNORE;IGNORE;<U0040> # 82 @
<U00A4> IGNORE;IGNORE;IGNORE;<U00A4> # 83 ¤
<U00A2> IGNORE;IGNORE;IGNORE;<U00A2> # 84 ¢
<U0024> IGNORE;IGNORE;IGNORE;<U0024> # 85 $
<U00A3> IGNORE;IGNORE;IGNORE;<U00A3> # 86 £
<U00A5> IGNORE;IGNORE;IGNORE;<U00A5> # 87 ¥
<U20A0> IGNORE;IGNORE;IGNORE;<U20A0> # ecu
... 省略
<U20AF> IGNORE;IGNORE;IGNORE;<U20AF> # drachma
<U002A> IGNORE;IGNORE;IGNORE;<U002A> # 88 *
<U005C> IGNORE;IGNORE;IGNORE;<U005C> # 89
<U0026> IGNORE;IGNORE;IGNORE;<U0026> # 90 &
<U0023> IGNORE;IGNORE;IGNORE;<U0023> # 91 #
<U0025> IGNORE;IGNORE;IGNORE;<U0025> # 92 %
<U207B> IGNORE;IGNORE;IGNORE;<U207D> # 93 <-S>
<U002B> IGNORE;IGNORE;IGNORE;<U002B> # 94 +
<U207A> IGNORE;IGNORE;IGNORE;<U207E> # 95 <+S>
<U00B1> IGNORE;IGNORE;IGNORE;<U00B1> # 96 ±
<U00B4> IGNORE;IGNORE;IGNORE;<0> # 123 ´
<U0060> IGNORE;IGNORE;IGNORE;<1> # 124 `
<U0306> IGNORE;IGNORE;IGNORE;<2> # 125 <"(>
<U005E> IGNORE;IGNORE;IGNORE;<3> # 126 ^
<U030C> IGNORE;IGNORE;IGNORE;<4> # 127 <"<>
<U030A> IGNORE;IGNORE;IGNORE;<5> # 128 <"0>
<U00A8> IGNORE;IGNORE;IGNORE;<6> # 129 ¨
<U030B> IGNORE;IGNORE;IGNORE;<7> # 130 <"">
<U007E> IGNORE;IGNORE;IGNORE;<8> # 131 ~
<U0307> IGNORE;IGNORE;IGNORE;<9> # 132 <".>
<U00F7> IGNORE;IGNORE;IGNORE;<a> # 133 ¸
<U00D7> IGNORE;IGNORE;IGNORE;<b> # 134 ´
<U2260> IGNORE;IGNORE;IGNORE;<c> # 135 <!=>
<U003C> IGNORE;IGNORE;IGNORE;<d> # 136 <
<U2264> IGNORE;IGNORE;IGNORE;<e> # 137 <=<>
<U003D> IGNORE;IGNORE;IGNORE;<f> # 138 =
<U2265> IGNORE;IGNORE;IGNORE;<g> # 139 </>=>
<U003E> IGNORE;IGNORE;IGNORE;<h> # 140 >
<U00AC> IGNORE;IGNORE;IGNORE;<i> # 141 ¬
<U007C> IGNORE;IGNORE;IGNORE;<j> # 142 |
<U00A6> IGNORE;IGNORE;IGNORE;<k> # 143 |
<U00B0> IGNORE;IGNORE;IGNORE;<l> # 144 °
<U00B5> IGNORE;IGNORE;IGNORE;<m> # 145 m
<U2126> IGNORE;IGNORE;IGNORE;<n> # 146 <Om>
... 省略


回顾:

xb@dnxb:/tmp/test/sub$ LC_COLLATE=en_US.UTF-8 ls
`  ×  <  =  >  -  ,  ;  :  !  ?  ¿  "  «  ]  {  \  #  1  2  a  b  m
xb@dnxb:/tmp/test/sub$


你可以发现它第一组是 <U0020> IGNORE;IGNORE;IGNORE;<U0020> # 32 <SP> 至 <U00B4> IGNORE;IGNORE;IGNORE;<0> # 123 ´ , 之后是第二组。第二组轻于第一组。第二组 ` × < = > 除了奇怪的 × 外,其它都是跟 en_US.UTF-8 的 ls 排序吻合 (我猜是 weight 越少越轻,所以往上排序)。之后第一组的 - , ; : ! ? ¿ " « ] { \ # ( `\` 是 005c,# 看不见),全和 en_US.UTF-8 的 ls 排序吻合。

文档说的 forward;backward 有点生涩, 所以我也没完全理解排序原理。可以S/O搜其它的解释, 自己理解:
Take a look at how a and A are ordered based on their entries in iso14651_t1_common:
<U0061> <a>;<BAS>;<MIN>;IGNORE # 198 a
<U0041> <a>;<BAS>;<CAP>;IGNORE # 517 A

b and B are similar:
<U0062> <b>;<BAS>;<MIN>;IGNORE # 233 b
<U0042> <b>;<BAS>;<CAP>;IGNORE # 550 B

We see that on the first pass, both a and A have the collating symbol <a>, while both b and B have the collating symbol <b>. Since <a> appears before <b> in iso14651_t1_common, a and A are tied before b and B. The second pass doesn't break the ties because all four characters have the collating symbol <BAS>, but during the third pass the ties are resolved because the collating symbol for lowercase letters <MIN> appears on line 3467, before the collating symbol for uppercase letters <CAP> (line 3488). So the sort order ends up as a, A, b, B.
Swapping the first and third collating symbols would sort letters first by case (lower then upper), then by accent (<BAS> means non-accented), then by alphabetical order. However, both <MIN> and <CAP> come before the numeric digits, so this would have the unwanted effect of putting digits after letters.

The easiest way to keep digits first while making all lowercase letters come before all uppercase letters is to force all letters to tie during the first comparison by setting them all equal to <a>. To make sure that they sort alphabetically within case, change the last collating symbol from IGNORE to the current first collating symbol. Following this pattern, a would become:
<U0061> <a>;<BAS>;<MIN>;<a> # 198 a

A would become:
<U0041> <a>;<BAS>;<CAP>;<a> # 517 A

b would become:
<U0062> <a>;<BAS>;<MIN>;<b> # 233 b

B would become:
<U0042> <a>;<BAS>;<CAP>;<b> # 550 B
and so on for the rest of the letters.


文档有解释 weight  不过也是生涩 ,  ibm 有更适合我这种小白理解的介绍:

    Each single-byte character in a database is represented internally as a unique number between 0 and 255 (in hexadecimal  notation, between X'00' and X'FF'). This number is referred to as the code point of the character; the assignment of numbers to  characters in a set is collectively called a code page. A collating sequence is a mapping between the code point and the desired position of each character in a sorted sequence.  The numeric value of the position is called the weight of the character in the collating  sequence.  In the simplest collating sequence, the weights are identical to the code points.  This is called the identity sequence.

    For example, suppose the characters B and b have the code points X'42' and X'62',
    respectively.  If (according to the collating sequence table) they both have a sort weight of X'42' (B), they collate the same.  If the sort weight for B is X'9E', and the sort weight for b is X'9D', b will be sorted before B.  The collating sequence table specifies the weight of each character.  The table is different from a code page, which specifies the code point of each character. Consider the following example.  The ASCII characters A through Z are represented by X'41' through X'5A'.  To describe a collating sequence in which these characters are sorted consecutively (no intervening characters), you can write: X'41', X'42',
    … X'59', X'5A'.

    The hexadecimal value of a multibyte character is also used as the weight. For example, suppose the code points for the double-byte characters A and B are X'8260' and X'8261'  respectively, then the collation weights for X'82', X'60', and X'61' are used to sort these two  characters according to their code points. The weights in a collating sequence need not be unique.  For example, you could give uppercase letters and their lowercase equivalents the
    same weight.


至于为什么排序要 locale, iso 文档提出了一些问题例子, 如文化/口音的不同,排序也要求不同:

...
 Sorted  Internal
       List    Values
       Aaaa    01010101
       abbb    01030303
       Aaaa    02010101
       Abbb    02030303
This is also predictable, but remains obviously incorrect for any country with regard to cultural

最后,让我们自制 locales。甭管 C 或 en_US.UTF-8, {} 都是重过 [], 现在我们把它们俩颠倒:

xb@dnxb:~$ mkdir ~/.xiaobai_locale
xb@dnxb:~$ cd ~/.xiaobai_locale
xb@dnxb:~/.xiaobai_locale$ cp /usr/share/i18n/locales/en_US  ~/.xiaobai_locale/
xb@dnxb:~/.xiaobai_locale$ cp /usr/share/i18n/locales/iso14651_t1*  ~/.xiaobai_locale/
xb@dnxb:~/.xiaobai_locale$ grep  '# 73 \[' iso14651_t1_common -A 4
<U005B> IGNORE;IGNORE;IGNORE;<U005B> # 73 [
<U005D> IGNORE;IGNORE;IGNORE;<U005D> # 74 ]
<U007B> IGNORE;IGNORE;IGNORE;<U007B> # 75 {
<U007D> IGNORE;IGNORE;IGNORE;<U007D> # 76 }
<U00A7> IGNORE;IGNORE;IGNORE;<U00A7> # 77 §
xb@dnxb:~/.xiaobai_locale$ touch '[' ']' '{' '}'
xb@dnxb:~/.xiaobai_locale$ ls
[  ]  {  }  en_US  iso14651_t1  iso14651_t1_common  iso14651_t1_pinyin
xb@dnxb:~/.xiaobai_locale$ vimx iso14651_t1_common #把 { 和 } 拉上
xb@dnxb:~/.xiaobai_locale$ grep  '# 75 {' iso14651_t1_common -A 4
<U007B> IGNORE;IGNORE;IGNORE;<U007B> # 75 {
<U007D> IGNORE;IGNORE;IGNORE;<U007D> # 76 }
<U005B> IGNORE;IGNORE;IGNORE;<U005B> # 73 [
<U005D> IGNORE;IGNORE;IGNORE;<U005D> # 74 ]
<U00A7> IGNORE;IGNORE;IGNORE;<U00A7> # 77 §
xb@dnxb:~/.xiaobai_locale$ localedef -i en_US -f UTF-8 -vc $HOME/.xiaobai_locale/en_HELLO.UTF-8
en_US:15: non-symbolic character value should not be used
... 省略
LC_CTYPE: table for width: 0 bytes
xb@dnxb:~/.xiaobai_locale$ ls
[  ]  {  }  en_HELLO.UTF-8  en_US  iso14651_t1  iso14651_t1_common  iso14651_t1_pinyin
xb@dnxb:~/.xiaobai_locale$ LOCPATH=$HOME/.xiaobai_locale LC_ALL=en_HELLO.UTF-8 ls
{  }  [  ]  en_HELLO.UTF-8  en_US  iso14651_t1  iso14651_t1_common  iso14651_t1_pinyin
xb@dnxb:~/.xiaobai_locale$

{} 变成 [] 的左边,实验成功 :)


再来把 '黄' 和 '晃' 颠倒。由于第一个 pass 的 '黄' <U9EC4> 重过 '晃' <U6643>, 所以把 '晃' 放在 '黄' 上面是无效的。可以把最后一个 pass 的 IGNORE 换成 <a> 和 <b>, 因为 <a> 轻过 <b>, 所以把 '黄' 改成 <a> 和 '晃' 改成 <b>,即可让 '晃' 重过 '黄', 不过'晃' 必须在 '黄' 下面 。至于 include template 有两种方法,一个是 copy, 另外一个是 script。由于 /usr/share/i18n/locales/iso14651_t1_pinyin 已有 HAN, 所以直接用 copy "iso5201314_pinyin" 以避免 HAN 命名冲突。我也跳过 iso14651_t1 中间代理, 直接用 en_HELLO include "iso5201314" 和 "iso5201314_pinyin":


xb@dnxb:~/.xiaobai_locale$ cp en_US en_HELLO

xb@dnxb:~/.xiaobai_locale$ grep iso en_HELLO -A 2 -B 1

% Copy the template from ISO/IEC 14651

copy "iso14651_t1"


END LC_COLLATE

xb@dnxb:~/.xiaobai_locale$ vimx en_HELLO

xb@dnxb:~/.xiaobai_locale$ grep iso en_HELLO -A 2 -B 1

% Copy the template from ISO/IEC 5201314

copy "iso5201314"

copy "iso5201314_pinyin"


END LC_COLLATE

xb@dnxb:~/.xiaobai_locale$ sed 's/14651/5201314/g' iso14651_t1_common > iso5201314

xb@dnxb:~/.xiaobai_locale$ sed 's/copy "iso14651_t1_common"//g' iso14651_t1_pinyin > iso5201314_pinyin

xb@dnxb:~/.xiaobai_locale$ touch '[' ']' '黄' '晃' '{' '}'

xb@dnxb:~/.xiaobai_locale$ ls '[' ']' '黄' '晃' '{' '}'

[  ]  {  }  晃  黄

xb@dnxb:~/.xiaobai_locale$ grep -En '晃|黄' iso5201314_pinyin

7523:<U9EC4> <U9EC4>;IGNORE;IGNORE;IGNORE       #黄20546

7535:<U6643> <U6643>;IGNORE;IGNORE;IGNORE       #晃2551

xb@dnxb:~/.xiaobai_locale$ vimx iso5201314_pinyin

xb@dnxb:~/.xiaobai_locale$ grep -En '晃|黄' iso5201314_pinyin

7523:<U9EC4> <U9EC4>;IGNORE;IGNORE;<a>  #黄20546

7535:<U6643> <U6643>;IGNORE;IGNORE;<b>  #晃2551

xb@dnxb:~/.xiaobai_locale$ sudo cp en_HELLO /usr/share/i18n/locales/

xb@dnxb:~/.xiaobai_locale$ sudo cp iso5201314* /usr/share/i18n/locales/

xb@dnxb:~/.xiaobai_locale$ sudo bash -c "echo 'en_HELLO.UTF-8 UTF-8' >> /etc/locale.gen"

xb@dnxb:~/.xiaobai_locale$ sudo locale-gen #更新 /usr/lib/locale/locale-archive

Generating locales (this might take a while)...

... 省略

  en_HELLO.UTF-8... done

Generation complete.

xb@dnxb:~/.xiaobai_locale$ sudo update-locale LANG=en_HELLO.UTF-8 #更改 locale 全局变量

xb@dnxb:~/.xiaobai_locale$ grep LANG /etc/default/locale

LANG=en_HELLO.UTF-8

xb@dnxb:~/.xiaobai_locale$ . /etc/default/locale #不用重启,现在测试

xb@dnxb:~/.xiaobai_locale$ ls '[' ']' '黄' '晃' '{' '}' #全部成功颠倒

{  }  [  ]  黄  晃

xb@dnxb:~/.xiaobai_locale$


最后的最后,把 "我爱你" 排序在所有字的最下方, 如:

<U8444> <U8444>;IGNORE;IGNORE;IGNORE    #葄0
<U888F> <U888F>;IGNORE;IGNORE;IGNORE    #袏0
<U963C> <U963C>;IGNORE;IGNORE;IGNORE    #阼0
<U3010> <U3010>;IGNORE;IGNORE;<d>   #【
<U6211> <U6211>;IGNORE;IGNORE;<a>   #我841127
<U7231> <U7231>;IGNORE;IGNORE;<b>   #爱60751
<U4F60> <U4F60>;IGNORE;IGNORE;<c>   #你313574
#
order_end
#
END LC_COLLATE


浪漫叻~


Wednesday, 18 April 2018

crontab 的排错 - 最快的姿势

常看见有人问 crontab 的问题。我觉得有必要写这篇文章。

crontab 排错事项通常有三点:
1. 置顶 SHELL=/bin/bash ,因为默认 shell 可能不支持你放在 crontab 的语法。
2. 命令和 shell 脚本加上 echo yyy >> /tmp/xxx.log (别忘了 >> 和 > 的区别) 或 touch /tmp/yyy.log (别忘了删除), 来确认你要执行的 crontab 命令或 shell 脚本有运行至重点部分。
3. 环境变量。

新手最意想不到的排错正是第三种,环境变量。在交互模式有环境变量促使某些命令能运行, crontab 默认没有给以足够的环境变量。

让我说说如何在 xubuntu 利用 crontab 换背景图。

终端输入命令 crontab -e (如果是问 editor, 就选 vim.tiny),然后敲 i 进入 insert mode, 输入:

SHELL=/bin/bash
* * * * * (PID=$(pgrep xfce4-session); export DBUS_SESSION_BUS_ADDRESS=$(grep -z DBUS_SESSION_BUS_ADDRESS /proc/$PID/environ|cut -d= -f2-); xfconf-query --channel xfce4-desktop --property /backdrop/screen0/monitor0/workspace0/last-image --set /home/xubuntu/Pictures/1.jpg)
* * * * * (sleep 5; PID=$(pgrep xfce4-session); export DBUS_SESSION_BUS_ADDRESS=$(grep -z DBUS_SESSION_BUS_ADDRESS /proc/$PID/environ|cut -d= -f2-); xfconf-query --channel xfce4-desktop --property /backdrop/screen0/monitor0/workspace0/last-image --set /home/xubuntu/Pictures/2.jpg)

然后 Esc,然后 :wq 储存出去。然后每1分钟的第一秒会换背景图成 /home/xubuntu/Pictures/1.jpg,然后等 5 秒会变成 /home/xubuntu/Pictures/2.jpg,然后等多 50 秒又变回去 1.jpg, ... 周而复始。

上面的 export DBUS_SESSION_BUS_ADDRESS=$(grep -z DBUS_SESSION_BUS_ADDRESS /proc/$PID/environ|cut -d= -f2-); 用途是什么 ?

用途是换背景图需要定义 DBUS_SESSION_BUS_ADDRESS 这个环境变量。

在终端交互模式输入 env 就能知道当前的变量,再和 crontab 的 env > /tmp/env.log 做个比较,就能知晓 crontab 默认的环境变量是很少的。

如何知晓换背景图需要 DBUS_SESSION_BUS_ADDRESS 这个关键的环境变量 ? 你可以用我独创的姿势:

a. declare -p > /tmp/d.sh
env > /tmp/d.sh 会卸掉引号所以不适合直接用, 所以才选择 declare -p > /tmp/d.sh。内容里的 declare -x 恰好就是 export 。

b. 在 /tmp/test.sh 输入 (eog 程序拿来看图, 只是例子):

  . /tmp/d.sh
  eog /home/xiaobai/Pictures/1.jpg

c. 最后不停测试如下。 如果成功显示图片,就删除 /tmp/d.sh 一半的内容; 反之则 undo 再删除另一半, 或一半的一半:
  env -i bash /tmp/test.sh
原理是 env -i 去除全部环境变量,然后 source (点号) 那新的环境变量让脚本使用。
d. 最后就能快速锁定出最关键的环境变量, 可以是多个环境变量, 比如 eog 需要 DBUS_SESSION_BUS_ADDRESS 和 DISPLAY。
declare -x DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/1000/bus"
declare -x DISPLAY=":0"
缺少 DBUS_SESSION_BUS_ADDRESS 并非看不见图片,而是延迟,所以环境变量缺少的后果可能并非那么直观。

最后,crontab 的默认环境变量可能会影响,且 env -i 并非完全没有环境变量,你必须考量这种可能性。

Sunday, 28 January 2018

电脑系统,程序语言,网络断点史


1822 年, Charles Babbage 研发差分机(自动化算术)。 英国政府赞助。
1832 年, 差分机只能完成 1/7
1837 年, Charles Babbage 研发出一个电脑, Mechanical computer (Analytical Engine), 不过还是未完成。
1842 年, Ada Lovelace 数学家帮忙 Charles Babbage, 她的笔记被公认为是第一个计算机程序。
1906 年, International Electrotechnical Commission (IEC) 国际电工委员会成立, 负责有关电气工程和电子工程领域的国际标准化工作。
1911 年, Marchant calculator 被私人公司研发。
1932 年, International Telephone Consultative Committee (CCIF) 和 International Telegraph Consultative Committee (CCIT) 合并成国际电信联盟 International Telecommunication Union (ITU) 的机构: 国际电话电报咨询委员会 International Telegraph and Telephone Consultative Committee (CCITT), 标准化除了无线电的电信。
1935-1938 年, 德国人 Konrad Zuse 自己研发出 mechanical calculator Z1
1936 年, 苏联开发出水流积算器(Water Integrator), 可运算 non-homogeneous differential equations
1940 年, Konrad Zuse 展出 Z2
1941 年, Konrad Zuse 展出德国政府赞助的可编程的通用型计算机 Z3
1942 年, Konrad Zuse 开始研发 Z4
1943 年, 美军赞助宾夕法尼亚大学团队开发 ENIAC 计算机。 Z1 被轰炸。
1945 年, Z4 附近地区被敌军轰炸而停滞。1949 年才继续研发出来。
1946 年,
  • 轨迹球 (trackball) 为了雷达系统开发出来。
  • ENIAC 开发完成。
1948 年,苏联 20 人团队开发 MESM 可编程通用型电脑。1950 年开发完成。
1949 年,
  • Moniac 液态 Analog computer 被纽西兰人 William Phillips 开发用来预测英国经济
  • 英国原子能科学研究院 (AERE) 开始开发 Harwell computer
  • ITU 正式纳入联合国专门机构之一。
1951 年, Harwell computer 开发完成。1957 年从哈韦尔搬家到比赛赢家胡弗汉顿大学。改名为 WITCH。1973 年捐去伯明翰博物馆。 2012 年在英国国家计算机博物馆(TNMOC) 重启。
1952 年, MIT 科学博士 David A. Huffman 发表了 A Method for the Construction of Minimum-Redundancy Codes 论文,演算法应用被称为霍夫曼编码(Huffman coding) , 用于无损数据压缩。
1953 年, IBM 的 John Backus 为了 IBM 701 大型计算机开发了第一个高级程序语言 Speedcoding。 之后 John Backus 团队为了 IBM 704 开始开发 FORTRAN 语言
1954 年,日本推出继电器计算机 FACOM 100。
1955 年,
  • 美国海军 Whirlwind Project 在 MIT 开发了可在屏幕画图的 Light Pen
  • MIT 研发 Tx-0 (Transistorized Experimental computer zero) 电脑。
1956 年, 兰德公司和卡内基梅隆大学开发出 Information Processing Language (IPL), 第一个 AI 程序语言
1957 年, 哈佛 Kenneth E. Iverson 已着手开发 APL 语言(1962 年在 A Programming Language 一书公开)。
1958 年,
  • MIT John McCarthy 研发出 Lisp 语言
  • John Backus 用 Backus–Naur form 帮助苏黎世联邦理工学院开发出 ALGOL 语言 (即 ALGOL 58)。
1959 年,
  • Lisp 推出 GC (Garbage Collection) 垃圾回收机制。
  • CODASYL 联盟推出 COBOL (common business-oriented language) 语言。
  • Digital Equipment Corporation(DEC)基于 Tx-0 研发 PDP-1
1960 年, 在巴黎推出 ALGOL 60
1962 年,
  • DEC 推出 PDP-4。
  • MIT 设计, DEC 和 Spear 推出的第一台迷你电脑 (12-bit) LINC (命名来源于 MIT Lincoln Laboratory)。
1963 年,
  • DEC 推出 (12-bit) PDP-5 和 (36-bit) PDP-6。
  • MIT Lincoln TX-2 电脑的一个叫 Sketchpad 程序有 light pen 画图功能。
  • ESR 教育公司推出 5 美金的玩具型电脑 Digi-Comp I。
  • 剑桥和伦敦大学联手基于 ALGOL 60 开发出 CPL (Combined Programming Language) 语言。
1964 年,
  • DEC 推出 PDP-7
  • General Electric, Bell Labs, MIT 联手计划开发 Multics 系统。
  • IBM 开发了 PL/I 语言 (Programming Language One)。
  • 达特茅斯学院 Kemeny & Kurtz 开发了 BASIC 语言
1965 年, DEC 推出热销的商用 12-bit 电脑 PDP-8。
1966 年,
  • 基于 ALGOL 60 的 ALGOL W 语言面世。
  • IBM 推出 DOS/360 系统, 首个磁盘操作系统(Disk Operating System,DOS)。
1967 年, 剑桥教授 Martin Richards 基于 CPL 开发出 BCPL (Basic Combined Programming Language) 语言。
1968 年,
  • 德国 Telefunken 公司开发了 ball-based mouse。 Mouse 出现在斯坦福研究所开发的 NLS 可点击超链接功能的系统。
  • 几所大学合作的基于 PL/I 的 XPL 语言出现,用来学习开发编译器。
1969 年, Multics 的几个研究员放弃 Multics 而专帮 Bell Labs 重新开发新的 OS 叫 UNIX。 Ken Thompson 在 PDP-7 以汇编开发了 UNIX 系统(命名来源: Multics -> Unics -> Unix)。
1970 年,
  • UNIX 1 从 PDP-7 移植到 PDP-11/20。
  • Niklaus Wirth 基于 ALGOL W 开发出 Pascal 语言
  • IBM 的 Edgar F. Codd 论文提出关系模型 (Relational model) 。
1971 年,出版 Unix 手册, 已有 mail, cp, su 命令, 在 Thompson shell 执行。
1972 年,
  • Thompson 参考 BCPL 研发出 B 语言, 不过用在 PDP-11 有问题。 Dennis Ritchie 根据 B+types 开发出新的 C 语言。 Thompson 使用时失败了 3 次, Dennis 回去加上了 Structures 后才成功。 UNIX 2 在十台电脑安装。 有了 echo 命令和 c 编译器。 UNIX 3 推广 C 语言。 同年 UNIX 4 从汇编语言大量改成 C 语言。
  • Paul Allen 和 Bill Gates 成立 Traf-O-Data 公司。
1973 年,
  • Mouse 不再停留在只点击链接, Xerox PARC 推出了以 Mouse+GUI 为主要界面的 Xerox Alto 电脑。
  • Intel 顾问 Gary Kildall 帮 Intel 研发 PL/M (Programming Language for Microcomputers)语言。
1974 年,
  • UNIX 5 大量发出教育 license。 有了 dd, find 命令。
  • 32-bit OS/32 系统的 Interdata 7/32 推出。
  • Gary Kildall 以自己的语言 PL/M 开发出 CP/M (Control Program/Monitor), 首个能在微型电脑运行的 DOS (磁盘操作系统)。
1975 年,
  • 两个 Berkeley 研究生 Bill Joy 和 Chuck Haley 接触了 Ken Thompson 所带来的 UNIX 6 和 Pascal, 且改进了编辑器,称为 ex 。
  • Unix Users Group 协会成立。
  • Bill Gates 和 Paul Allen 看到 Altair 报道, 就联络 Altair。 在 PDP-10 30 天内完成Altair 的 BASIC 翻译器。 三月, 要展示顾客的飞机上才想起忘了写 bootstrap 程序(年初还未有从 ROM boot), Allen 机上手写 21 bytes 的 Intel 8080 汇编, 每 1 个 byte 三位数八进制代表一个指令。 展示时 PRINT 2+2 和 101 BASIC Computer Games 书的 35 行代码登月游戏 ROCKET 成功运行。 4月4日, "Micro-Soft" 成立 (“Micro”computer “Soft”ware)。 其基于 CP/M 系统(OS 基于 PL/M 语言) 的 Altair 8800 电脑推出。

35 行代码登月游戏 ROCKET


1976 年,
  • Steve Jobs 推出 Steve Wozniak 一手设计的 Apple 1 电脑, 用 BASIC 编译。
  • Bell labs 的 Stephen Bourne 开始开发 Bourne Shell (sh)
1977 年,
  • Bill Joy 编译了和 UNIX 类似的 BSD 系统
  • 商业化Unix 6 有了 ratfor 和 bc 语言。
  • Unix Users Group 协会因 "UNIX" 商标问题改名为 USENIX
  • Commodore 国际推出 Commodore BASIC 系统的 Commodore PET 电脑。
  • DEC 推出流行的 32-bit 电脑 VAX-11/780
  • UNIX 移植在 Interdata 7/32 和 Interdata 8/32。
  • 1977 至 1983 年, Jean Ichbiah 带队替美国国防部(DoD) 设计 Ada 语言, 以代替该部成百的语言。
  • Larry J. Ellison 拜读了 1970 IBM Codd 的论文后, 和 Ampex 前上司 Robert Nimrod "Bob" Miner 等人成立软体开发实验室 (Software Development Laboratories, SDL)。
  • Abraham Lempel 和 Jacob Ziv 的论文 A Universal Algorithm for Sequential Data Compression 提出以他们名字字母(lz不是楼主囧)和年份为名的无损数据压缩算法 LZ77
1978 年,
  • 苹果计划 Apple Lisa (有 GUI 的)电脑。
  • UNIX 7 把 Thompson shell 换成了 Bourne Shell, 有了 sed 和 awk 命令。
  • Bell Labs 卖 UNIX 7 license 给 Microsoft
  • Dennis Ritchie 和 Brian Kernighan (两人简称 K&R) 出版了著名的 "The C Programming Language" 第一版。
  • Bill Joy 推出了基于 UNIX 6 的 1BSD
  • Lempel 和 Ziv 隔年的论文 Compression of Individual Sequences Variable-Rate Coding 再次提出无损数据压缩算法 LZ78
1979 年,
  • UNIX 移植在 DEC VAX, 成为 UNIX/32V
  • Bill Joy 五月推出 2BSD, 有了 vi 和 C shell。
  • UNIX/32V 工具+2BSD 工具+虚拟内存(大改)内核的 3BSD 在年尾推出。
  • 生产 CP/M 硬件的 Novell 公司成立。
  • Bjarne Stroustrup 开发 C with Classes 语言
  • UNIX 7 发布包括了 Mike Lesk 在 1978 写的 UUCP (Unix-to-Unix Copy) 程序。
  • 北卡罗来纳大学研究生 Steve Bellovin 出席杜克大学研究生 Jim Ellis 和 Tom Truscott 的开发类似 ARPANET 的邮件列表(mailing lists) 以提供没参与 DARPA (国防高级研究计划局) 项目的大学会议, 他先以 Bourne Shell 语言写了 Netnews 程序,能利用 UUCP 程序通过调制解调器 (modem) 让两个电脑交换文件, 较后才以 C 语言重写。 1980 年的 Usenet 因此最先应用在这两所大学之间。
  • Larry J. Ellison 改 SDL 公司名为 Relational Software, Inc. (RSI) , 推出在 PDP-11 运行的 Oracle V2 (心理学, 没有人喜欢当小白鼠掏钱买五人小公司的 V1), 乃是首个商用关系数据库管理系统 (relational-database management system, RDBMS), 首个客户是 CIA (IBM 还没准备卖, 所以才找到这家做着类似东西的公司, 负责找的 Dave Roberts 恰好是 Miner 前上司, Oracle 是 CIA 项目的 codename)。
1980 年,
  • Microsoft 推出 UNIX 7 license 的 Xenix 系统, 卖给 IBM/Intel/Tandy/Siemens (Siemens 改 Xenix 成 SINIX) 等 OEMs 公司。
  • 英国推出 Sinclair BASIC 系统的 Sinclair ZX80 电脑。
  • Commodore VIC-20 推出。 首个销售超过百万台的电脑。
  • 欧洲核子研究组织(CERN) 的 Tim Berners-Lee 写了超文本 (hypertext, 虽然当时没有这么叫) 程序 ENQUIRE。
  • 二月, IEEE (Institute of Electrical and Electronics Engineers, 电气和电子工程师协会)的 IEEE Project 802 LAN/MAN Standards Committee(LMSC) 第一次开会 , "802" 纯粹是 IEEE 的下一个规范项目的数字, 比如 "803" 是 1983 年的 IEEE 803-1983 的 IEEE Recommended practice for unique identification in power plants and related facilities - principles and definitions(发电厂及相关设施特殊标识的 IEEE 推荐规程 - 原则与定义), "800" 是 AIEE Test Code for D-C Aircraft Rotating, 不过没有 801 不懂做么。
  • UUCP 协议的 Usenet 新闻网公开。
  • Seattle Computer Products 的 Tim Paterson 在 8086 CPU 开发与 CP/M 系统类似的 QDOS (Quick and Dirty Operating System) 系统, 较后改名为 86-DOS, 其 1.14 版本有 4000 行汇编代码。
1981 年,
  • Bill Gates 在 COMDEX 展览会发掘了 VisiCorp 公司在 IBM 电脑运行的 Visi On GUI
  • Tim Paterson 加入微软后, 把 86-DOS 1.10 版权卖给微软, 修改后更名为 MS-DOS 在 IBM 电脑运行。 Gary Kildall 不满意 IBM 提出的 $200,000 价钱买断他的 CP/M, 结果微软以 $50,000 低价卖 MS-DOS 授权给 IBM, 称为 PC DOS
  • Sinclair ZX 81 推出。
  • IBM 姗姗来迟 (比 Oracle 迟了, 虽然 1970 论文是 IBM 写的) 推出 SQL/DS (Structured Query Language/Data System), 它的首个 RDBMS。
1982 年,
  • 大杂烩(PWB/UNIX 2.0, CB UNIX 3.0, UNIX/TS 3.0.1, UNIX/32V 混在一起) UNIX System III 系统面世。
  • Sinclair ZX Spectrum 推出。
  • 鉴于主要产品 Oracle 名气大, RSI 公司第二次改名成 Oracle Systems Corporation。
  • Sun Microsystems 公司成立, Bill Joy 很快就加入。 Sun 同年卖出 Sun-1 工作站, 乃是创办人之一 Andy Bechtolsheim 在斯坦福大学 DARPA 赞助下设计的 CPU board, 系统是 UniSoft 移殖 Unix 7 的 UniPlus V7 到 Motorola 68000 微处理器 SunOS。
  • James A. Storer 和 Homas G. Szymanski 发表论文 Data Compression via Textual Substitution, 从 LZ77 衍生的无损数据压缩算法 Lempel–Ziv–Storer–Szymanski (LZSS)。 字母越来越长了, 因为累积了四个作者囧。
1983 年,
  • 著名的商用 UNIX System V ( SysV) 系统面世。 第一版叫 System V Release 1 或简称 SVR1。 总共到 1997 年的 SVR5。
  • MIT 的 Richard Stallman 展开了 GNU Project,专门开发免费 license 的 UNIX-like(很像 UNIX 但又不是 UNIX,GNU 也是 GNU's Not Unix 的递归缩写) 工具。
  • Apple Lisa 发布。
  • 4.2 BSD 推出。 SunOS 曾一度融合 SysV 和 4.2 BSD。
  • Anders Hejlsberg 在 Borland 幂下开发 Turbo Pascal 和 Delphi 两个IDE (之后在微软带队开发了 .Net, C#, TypeScript)。
  • Novell 开发网络系统 NetWare
  • "C with Classes" 重命名为 C++
  • 由于 CIA 和 Navy Intelligence (海军情报)都要求移植在 VAX, Navy 还要支持 Unix, 以可移植性的 C 语言重写成 Oracle v3。 可移植性让 Oracle 不需要像 IBM 那样浪费时间维护 大型机和 VM(虚拟机系统) 等不同的机器。
1984 年,
  • Sinclair QDOS 系统的 Sinclair QL 推出。 Linus Torvalds 也用过 QL, 还写了自己的汇编器和编辑器。 他 11 岁就接触日本推出的 Commodore VIC-20 电脑了。
  • Lempel, Ziv (皆 LZ78 作者) 和 Terry Welch 发表论文 A Technique for High-Performance Data Compression, 从 LZ78 衍生的无损数据压缩算法 Lempel–Ziv–Welch (LZW) 。 简单, 快速, 用于 compress 命令 和 GIF。
1985 年,
1986 年,
  • 一月, IETF (Internet Engineering Task Force, 互联网工程任务组) 成立。
  • 六月, 4.3BSD 推出, 其 TCP/IP stack 影响力甚大。
  • IBM RT PC 工作站推出。 三种系统, 分别是 AIX version 1, AOS (Academic Operating System), 和 Pick (开发者之一名字 Dick Pick)。 AIX (Advanced Interactive eXecutive) 系统基于 UNIX SVR1|2 + 4.2|3BSD。
1987 年,
  • 阿姆斯特丹自由大学教授 Andrew S. Tanenbaum 开发了 MINIX (MINi-unIX), 在 IBM 运行的微内核迷你 UNIX 系统方便给学生学习。
  • OS/2 开发出来了。
  • AmigaOS v1.2/1.3 系统的 Commodore Amiga 500 推出, 乃是 Amiga 系列最畅销的电脑。
1988 年,
  • K&R 改版 ANSI 标准的 "The C Programming Language" 书出版。
  • AT&T (Bell labs 母公司)和 Sun 融合四巨头系统 BSD + SVR3 + Xenix + SunOS 变身为超级大杂烩, 称为 System V Release 4 (SVR4)。 在 Sun 的 SPARC (Scalable Processor ARChitecture) RISC指令集架构则称为 Solaris 2
  • Steve Jobs 1985 年建立的 NeXT Inc. 推出 NeXT Computer 工作站电脑。
  • Tim Paterson 抄袭 (1980) CP/M 卖给微软的 MS-DOS (1981) 且用来抢 IBM 客户的那件事还没完。 严重不爽微软 的 Gary Kildall 推出第一版就跟 MS-DOS 一样版本号的 DR DOS 3.31。 微软也不是省油的灯, Windows 在 DR DOS 运行会有警告, 且制造商需要付费或失去折扣才能运行 Windows 在非 MS-DOS 的系统 (注: Windows 95 之前 Windows 是在 DOS 上运行的 GUI 程序而不是完整的操作系统, 所以 DR DOS 可以有机可乘运行 Windows)。 DR DOS 因此不是对手, 不过也埋下了日后的官司
  • 在合作开发 OS/2 的过程, 由于与 IBM 各种意见不同包括文化以及需要打败移植性强的 UNIX,微软请来了 DEC 的 VMS 架构师 Dave Cutler 团队开发新的 NT (New Technology) , 即变成 NT OS/2。 他本人也不希望做类似 DOS plus 的架构而是全新的 NT。
1989 年, 为了移除 AT&T license,BSD 推出 BSD license 的 Networking Release 1 (Net/1) 系统。
1990 年,
  • GNU 要开发自家的 GNU Hurd 系统内核(也是参考 Mach)代替 UNIX。
  • 首个硬盘预安装和在保护模式 (Protected Mode) 运行的 Windows 3.0 系统首年就百万销售量,打败竞争对手 Macintosh。
1991 年,
  • 几乎砍掉所有 AT&T 工具的 Net/2 发行。
  • 几个月后,芬兰大学生 Linus Torvalds 不爽 MINIX 教育-only license ,在 MINIX 平台写了 Linux 系统内核, 8月26日在新闻组(Usenet newsgroup )正式分享 (7月3日已提过,不过目的是求人提供最新的 POSIX 标准文件)。 Linux 系统大量安装了 GNU 的工具,所以 Richard Stallman 坚持人们称呼 Linux 系统为 GNU/Linux 比较公平。
  • 随着 GNU GPL license 的 Linux 面世后, GNU Hurd 内核变成开发停滞
  • Berners-Lee 1989 年草拟,结合超文本 ( hypertext) + 传输控制协议(TCP) + 域名系統(DNS) 的三大主要概念组成 World Wide Web (WWW), 第一个万维网网站 http://info.cern.ch 由欧洲核子研究组织(CERN)的 NeXT 主机在8月6日上线。
  • 一月,IBM 知道微软有二心, 即独干改名成 Windows NT 的 NT OS/2 3.0。 IBM 就分手独自继续开发 OS/2 2.0。 十月, 微软推出 Windows 3.0 with Multimedia Extensions 1.0 支持声卡和 CD-ROM。
1992 年,
  • Berkeley Software Design (BSDi) 敢敢拿 Net/2 当成 BSD/386 来卖钱后,被 AT&T 控诉
  • 四月, Windows 3.1 两个月就破百万销售量。 新增 Multitasking 窗口和 Windows Registry
1993 年,
  • Novell 从 AT&T 买下了 Unix System Laboratories 分部。 继承者 FreeBSD, NetBSD 在官司期间已相续拔剑而出。
  • 美国国家超级电脑应用中心(NCSA)为 NCSA HTTPd web 服务器开发 CGI 标准。
  • 七月, NT 终于完成, Microsoft Windows NT 3.1 推出, 脱离 DOS 独立成为完美 32-bits 系统。 抢占式多任务 (Preemptive multitasking) 取代 Windows 1.0 起的协作式多任务 (Cooperative multitasking), 避免 CPU 占用而系统死锁。 与 IBM 分手后从 OS/2 的 “高性能文件系统” (High Performance File System, HPFS) 继续开发的 “新技术文件系统” (New Technology File System, NTFS) 取代一直以来用的 “文件配置表” (File Allocation Table, FAT)。 NT 当下只提供服务器版本, 不过未来的 Windows 2000 起 (不包括 Windows 9x 家族的 Me) 都是 NT。
  • ITU 的 CCITT 机构改成电信标准化部门 Telecommunication Standardization Sector,简称 (ITU-T)。
1994 年,
  • BSD 和 Novell 和解后已失去开源开发者的信心, 与 Linux 竞争已无可能。
  • Solaris 2.4 发行。
  • 世界上最好的语言终于刷存在感了, Rasmus Lerdorf 以 C 语言开发 Personal Home Page Tools (PHP Tools) 的一套 CGI 二进制工具。
1995 年,
  • Berkeley 大学最后一个版本 4.4BSD-Lite Release 2 发行。
  • Oracle Systems Corporation 改名改上瘾, 第三次改名成现今的 Oracle Corporation。
  • Amazon.com 卖出它的第一本书 “Fluid Concepts and Creative Analogies”。
1996 年,
  • OpenBSD 推出。
  • Sun 发布 Java 1.0
  • Compaq 内部文件提出 “Cloud Computing” 云计算。
  • 史丹佛大学生 Larry Page 参与 Stanford Digital Library Project (SDLP) 项目。 他觉得 WWW 就是巨大的链接图像, 所以打算打造反向链接 (Backlink) PageRank 算法的搜索引擎 BackRub 以取代当今流行的关键字搜索次数算法, 随后 Sergey Brin 加入一起以 Java 和 Python 语言开发。 爬虫在3月从 Larry Page 的史丹佛主页开始爬。 他们也参考李彦宏的 “link analysis”, 即 IDD Information Services 的 Randex 的 anchor text 且在 2001 的 patent 文件 提及 (点击 1998 年的下载只能看见 2001)。
1997 年, BackRub 改名成 Google , 然后在 9 月 15 日注册 google.com 域名。

1998 年,
  • Andi Gutmans 和 Zeev Suraski 与作者合作, 推出 PHP 3.0, 递归缩写命名为 PHP: Hypertext Preprocessor ,也取代了 PHP/FI 2.0 风格的命名。
  • 史丹佛大学博士生 Larry Page 和 Sergey Brin 年初和数人发布了 PageRank 算法论文。 接着两人成立 Google 公司, 首轮四个主要投资人包括 Sun 创办人之一 Andy Bechtolsheim 和 Amazon 创办人 Jeff Bezos。
  • 四月一日愚人节, IETF 发布超文本咖啡壶控制协议 (Hyper Text Coffee Pot Control Protocol (HTCPCP/1.0))。


1999 年,
  • Tim Berners-Lee 在 1995 年发出 "优化网络内容的传输方式" 挑战给 MIT Laboratory for Computer Science (LCS)。 应用数学教授 Tom Leighton 接受挑战。不过他和学生没想过开公司所以有点吊儿郎当, 只有 Daniel Lewin (911事件反抗被刺而成了首个遇难者) 认真写 Consistent hashing and random trees : algorithms for caching in distributed networks 硕士论文, 成为师徒署名的文档一部分。 1998 年, Lewin 和 Leighton 以此参加 MIT Sloan School of Management $50K 奖金的 Entrepreneurship Competition 年度比赛,打进一百队的六强,吸引了创投。选公司名选了夏威夷语是 “intelligent” 意思和口语是 "cool" 的 "Akamai" 。之后很快吸引了 Cisco, 微软, 苹果等的投资, 且微软以此要求软件开发包括在 NT 系统而不只是 Linux。开始只支持苹果 QuickTime stream 格式, 后来才加入 RealVideo 和 Windows Media 格式。 1999 年, Akamai Technologies, Inc. 因此推出了首个 CDN (Content Delivery Network,内容分发网络)。 请求路由算法包括 DNS-based request routing, HTML rewriting, Global Server Load Balancing, Dynamic metafile generation 和 anycasting。 准确 log 和 bill 是他们早期没想到的问题。

2000 年,
  • Ericsson 在新加坡发布第一部蓝牙手机 Ericsson T36。
  • 微软发布 .NET Framework 1.0 Beta 1。

2002 年, 微软发布 .NET Framework 1.0 Service Pack 1
2004 年, DragonFly BSD 推出。
2005 年,
  • Google 以至少 5千万美金收购 Android Inc. 公司。
  • 9月15日, 继承 Mozilla Application Suite 的 The SeaMonkey Internet Application Suite SeaMonkey 1.0 Alpha 推出。之所以叫 Suite 而不叫浏览器是因为它是一套的程序: 浏览器, 电邮, 新闻, IRC (ChatZilla) 集一身的客户端, 以及 HTML 编辑器,
2006 年, Amazon 推出 Amazon Web Services (AWS) 平台。 3 月推出 Amazon S3 (Amazon Simple Storage Service), 8月公测 EC2 (Amazon Elastic Compute Cloud)。
2008年,
  • 二月, Chris Wanstrath, P. J. Hyett, Tom Preston-Werner 和 Scott Chacon 以 Ruby 语言 的 Ruby on Rails MVC 应用框架开发出Git 托管服务的 Github
  • 10月20日, HTC 开售首个安卓系统(Linux Android) 的 HTC Dream (或 T-Mobile G1) 手机。
2009 年,NetWare v6.5 最后一版
2010 年, Oracle 收购 Sun
2011 年, 10 月 24 日, Gitlab 推出 gitlab v1.1 vmware image, 与 Github 不同之处在于它让你建立自己的 Git 托管服务器。 但是与 Github 一样是以 Ruby On Rails 开发(现包括 Go 和 Vue.js)。 由 Dmitriy 'DZ' Zaporozhets 和 Valery Sizov 九月开始开发
2017 年,十一月, 随着两台 AIX 系统的 IBM Flex System p460 [1][2]落榜, Linux 已 100% 占据 TOP500 列表的超级电脑系统。

Saturday, 30 September 2017

制造 fb live 时光机







如视频所示, 我可以下载打开 fb 直播之前的 15 分钟剧情 (从少林足球比赛进行中, 倒退 15 分钟 , 能看到刚刚开始集合的剧情。)

有时你打开直播, miss 掉之前重要的画面, 直播后也可能被隐藏或删除, 所以这招很有用。

Bash代码:


# rf: https://stackoverflow.com/questions/12498304/using-bash-to-display-a-progress-working-indicator
progressBarWidth=20
# Function to draw progress bar
progressBar () {

  taskCount="$1"
  tasksDone="$2"
  # Calculate number of fill/empty slots in the bar
  progress=$(echo "$progressBarWidth/$taskCount*$tasksDone" | bc -l)  
  fill=$(printf "%.0f\n" $progress)
  if [ $fill -gt $progressBarWidth ]; then
    fill=$progressBarWidth
  fi
  empty=$(($fill-$progressBarWidth))

  # Percentage Calculation
  percent=$(echo "100/$taskCount*$tasksDone" | bc -l)
  percent=$(printf "%0.2f\n" $percent)
  if [ $(echo "$percent>100" | bc) -gt 0 ]; then
    percent="100.00"
  fi

  # Output to screen
  printf "\r["
  printf "%${fill}s" '' | tr ' ' '#'
  printf "%${empty}s" '' | tr ' ' ' '
  printf "] $percent%% - $text "
}

fb_live_backward () 
{ 
    echo lili;
    ##no nid care if `firefox -no-remote -ProfileManager` then create lolo.default collision, overkill
    cookie_f='/tmp/fb_cookie';
    printf ".mode tabs \n
select host, case when host glob '.*' then 'TRUE' else 'FALSE' end, path, case when isSecure then 'TRUE' else 'FALSE' end, expiry, name, value
from moz_cookies where host = '.facebook.com';" | sqlite3 ~/.mozilla/firefox/*.default/cookies.sqlite > "$cookie_f";
    echo "$cookie_f";
    #echo > "$cookie_f";
    #curl -b "$cookie_f" -vLk "https://www.facebook.com/video/playback/playlist.m3u8?v=$vid" -H 'Host: www.facebook.com' -H 'User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:53.0) Gecko/20100101 Firefox/53.0' -H 'Accept: */*' -H 'Accept-Language: en-US,en;q=0.5' --compressed -H 'Referer: https://www.facebook.com/live' -H 'Connection: keep-alive'
    #curl -b "$cookie_f" -vLk 'https://video.fkul8-1.fna.fbcdn.net/hvideo-prn1/v/rvgvfRr3mfgtrG9-5YYe0/live-md/320741391732419.m3u8?_nc_rl=AfACJUPO37BnG6-k&oh=ca49b289cfe55cc22554f06974a11197&oe=597BC9DD'
    vid="$1";
    url="$(curl -b "$cookie_f" -s "https://www.facebook.com/video/playback/playlist.m3u8?v=$vid" -H 'Host: www.facebook.com' -H 'User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:53.0) Gecko/20100101 Firefox/53.0' -H 'Accept: */*' -H 'Accept-Language: en-US,en;q=0.5' --compressed -H 'Referer: https://www.facebook.com/live' -H 'Connection: keep-alive'  | tail -1)";
    if [[ $url != http* ]]; then
        echo "url failed";
    else
        curl -b "$cookie_f" -s "$url" | awk '/^#EXTINF/,/ts$/' > /tmp/tss;
        awk 'NR==2' /tmp/tss > /tmp/ts;
        awk 'NR==1 {first = $0} END {print}' /tmp/tss > /tmp/ts2;
        endI2="$(awk 'NR > 1 {print $1}' RS='__-' FS='.ts' /tmp/ts2 | tail -1)";
        endI="$(awk 'NR > 1 {print $1}' RS='__-' FS='.ts' /tmp/ts |  tail -1)";
        echo '###';
        echo "$endI";
        echo "$endI2";
        echo '#####';
        if [[ -z "$endI" || -z "$endI2" ]]; then
            echo "Abort.";
        else
            startI=$(($endI - 300)); #Backward up to 15 minutes * 60 seconds 
            if [ "$startI" -lt "0" ]; then
                startI=0;
            fi;
        fi;
        furl="$(awk -F'__-' '{print $1}' /tmp/ts)";
        startURL="${url%/*}";
        endURL="$(echo "${url##*/}" | awk -F? '{print $NF}')";
        rm /tmp/$vid.mp4 2> /dev/null;
        for n in `seq "$startI" "$endI2"`;
        do
            text=$(echo "Saving to /tmp/$vid.mp4 ..." `ls -lah /tmp/$vid.mp4 2>/dev/null | awk -F " " {'print $5'}` );
            progressBar $(($endI2 - $startI)) $(($n - $startI)) "$text";
            curl -b "$cookie_f" -s "$startURL"'/'"$furl"'__-'"$n"'.ts?'"$endURL" -H 'User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:53.0) Gecko/20100101 Firefox/53.0' -H 'Accept: */*' -H 'Accept-Language: en-US,en;q=0.5' --compressed -H 'Referer: https://www.facebook.com/live' -H 'Connection: keep-alive' >> /tmp/$vid.mp4;
        done;
        echo "Download completed :)";
    fi
}



我的一些相关笔记:




虽然这 2017 年的招已经 deprecated, 我也没时间研究 fb 新招, 不过希望可以带给你一点 idea 关于倒退直播的可能性 :D

Thursday, 31 August 2017

如何 crash 掉你朋友的 facebook app


#国庆日快乐 今天教大家如何 crash 掉你朋友的 facebook app :)


注: 请确保 update 最新的 facebook app (太旧版的没效)   至于 Android 版本, either nougat 和 lollipop 都中, 手头上没 marshmallow test 不过理论上应该一样中。


至于那条 link 是怎样拿的, 就留一手不公开了 :p 自己想 :)

Wednesday, 17 May 2017

在 fb 分享 app 链接的误区。

看到人家在 fb 推介自己的 app,突然间觉得有些话想说。




你按进去看到的不是 google play app, 而是 fb web browser。




解决方法是有的,让我们来做个实验。




按第一条 link(不是按图),你进到的就是 fb web browser。




按 "Install" 就会要求你 login, 不过我发现就算 login 了按 Install 也没反应。(按多几次出现叫我 install 在别的 device 的 dialog... 总之不稳定就是了)
[更新] 现在可以在右上角选在 play store 打开,不过仍然是麻烦, 多了一个步骤。




然而,如果按第二条 link, 就会先跳去 fb web browser 一秒, 再重定向去 google play app。




区别是什么 ? 分享链接的 https 的 s 字啦,丢掉它就可以 liao la。

还有一点,按预览图会直接跑 app(如果已安装),但如果你的目的是希望用户更新,那就应该把预览图关掉。

How to fixed - Not able to decompile apk in Ubuntu 17.04

I already knew that Oracle JDK is prefer over than OpenJDK long time ago, and now it give the solution of unable to decompile apk in Ubuntu 17.04.

xb@dnxb:~/Downloads$ java -jar ClassyShark.jar -open app-release.apk 
Exception in thread "main" java.lang.ExceptionInInitializerError
        at java.base/javax.crypto.JceSecurityManager.(JceSecurityManager.java:66)
        at java.base/javax.crypto.Cipher.getConfiguredPermission(Cipher.java:2610)
        at java.base/javax.crypto.Cipher.getMaxAllowedKeyLength(Cipher.java:2634)
        at java.base/sun.security.ssl.CipherSuite$BulkCipher.isUnlimited(CipherSuite.java:602)
        at java.base/sun.security.ssl.CipherSuite$BulkCipher.(CipherSuite.java:574)
        at java.base/sun.security.ssl.CipherSuite$BulkCipher.(CipherSuite.java:460)
        at java.base/sun.security.ssl.CipherSuite.(CipherSuite.java:1074)
        at java.base/sun.security.ssl.SSLContextImpl.getApplicableSupportedCipherSuiteList(SSLContextImpl.java:354)
        at java.base/sun.security.ssl.SSLContextImpl.access$100(SSLContextImpl.java:42)
        at java.base/sun.security.ssl.SSLContextImpl$AbstractTLSContext.(SSLContextImpl.java:590)
        at java.base/java.lang.Class.forName0(Native Method)
        at java.base/java.lang.Class.forName(Class.java:292)
        at java.base/java.security.Provider$Service.getImplClass(Provider.java:1844)
        at java.base/java.security.Provider$Service.newInstance(Provider.java:1820)
        at java.base/sun.security.jca.GetInstance.getInstance(GetInstance.java:236)
        at java.base/sun.security.jca.GetInstance.getInstance(GetInstance.java:164)
        at java.base/javax.net.ssl.SSLContext.getInstance(SSLContext.java:169)
        at okhttp3.OkHttpClient.(OkHttpClient.java:176)
        at okhttp3.OkHttpClient.(OkHttpClient.java:151)
        at retrofit2.Retrofit$Builder.build(Retrofit.java:551)
        at com.google.classyshark.updater.networking.NetworkManager.getGitHubApi(NetworkManager.java:29)
        at com.google.classyshark.updater.networking.AbstractDownloader.checkNewVersion(AbstractDownloader.java:35)
        at com.google.classyshark.updater.UpdateManager.checkVersion(UpdateManager.java:49)
        at com.google.classyshark.updater.UpdateManager.checkVersionGui(UpdateManager.java:45)
        at com.google.classyshark.gui.GuiMode.with(GuiMode.java:42)
        at com.google.classyshark.Main.main(Main.java:44)
Caused by: java.lang.SecurityException: Can not initialize cryptographic mechanism
        at java.base/javax.crypto.JceSecurity.(JceSecurity.java:118)
        ... 26 more
Caused by: java.lang.SecurityException: Can't read cryptographic policy directory: unlimited
        at java.base/javax.crypto.JceSecurity.setupJurisdictionPolicies(JceSecurity.java:324)
        at java.base/javax.crypto.JceSecurity.access$000(JceSecurity.java:73)
        at java.base/javax.crypto.JceSecurity$1.run(JceSecurity.java:109)
        at java.base/javax.crypto.JceSecurity$1.run(JceSecurity.java:106)
        at java.base/java.security.AccessController.doPrivileged(Native Method)
        at java.base/javax.crypto.JceSecurity.(JceSecurity.java:105)
        ... 26 more
xb@dnxb:~/Downloads$ echo $JAVA_HOME #useless :(
/usr/lib/jvm/java-1.9.0-openjdk-amd64
xb@dnxb:~/Downloads$ l /usr/lib/jvm/
total 32K
8792676 lrwxrwxrwx   1 root root ?   24 Mei  25  2016 default-java -> java-1.8.0-openjdk-amd64/
8792674 lrwxrwxrwx   1 root root ?   20 Mac   4 01:46 java-1.8.0-openjdk-amd64 -> java-8-openjdk-amd64/
8792533 -rw-r--r--   1 root root ? 2.7K Mac  17 07:09 .java-1.9.0-openjdk-amd64.jinfo
8792634 lrwxrwxrwx   1 root root ?   20 Mac  17 07:09 java-1.9.0-openjdk-amd64 -> java-9-openjdk-amd64/
8792534 drwxr-xr-x   8 root root ? 4.0K Apr  18 22:31 java-9-openjdk-amd64/
8792677 -rw-r--r--   1 root root ? 2.6K Mei   2 10:51 .java-1.8.0-openjdk-amd64.jinfo
8792673 drwxr-xr-x   7 root root ? 4.0K Mei  12 15:38 java-8-openjdk-amd64/
8792532 drwxr-xr-x   4 root root ? 4.0K Mei  12 15:38 ./
8650757 drwxr-xr-x 169 root root ?  12K Mei  17 02:53 ../
xb@dnxb:~/Downloads$ 
xb@dnxb:~/Downloads$ sudo tar xvzf jdk-8u131-linux-x64.tar.gz -C /usr/lib/jvm #download latest oracle jdk from http://www.oracle.com/technetwork/java/javase/downloads/
jdk1.8.0_131/
jdk1.8.0_131/javafx-src.zip
...
xb@dnxb:~/Downloads$ cat /etc/environment #add JAVA_HOME="/usr/lib/jvm/Your_jdk_directory_name/"
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games"
JAVA_HOME="/usr/lib/jvm/jdk1.8.0_131/"
xb@dnxb:~/Downloads$ grep '/etc/environment' ~/.bashrc #ensure got `source /etc/environment`, if no, `sudo vim /etc/environment` add that line.
source /etc/environment
xb@dnxb:~/Downloads$ . ~/.bashrc #reload ~/.bashrc
xb@dnxb:~/Downloads$ echo $JAVA_HOME #ensure $JAVA_HOME is printable
/usr/lib/jvm/jdk1.8.0_131/
xb@dnxb:~/Downloads$ sudo update-alternatives --install /usr/bin/java java ${JAVA_HOME%*/}/bin/java 20000 #credit: https://askubuntu.com/a/764914/265303
update-alternatives: using /usr/lib/jvm/jdk1.8.0_131/bin/java to provide /usr/bin/java (java) in auto mode
xb@dnxb:~/Downloads$ sudo update-alternatives --install /usr/bin/javac javac ${JAVA_HOME%*/}/bin/javac 20000
update-alternatives: using /usr/lib/jvm/jdk1.8.0_131/bin/javac to provide /usr/bin/javac (javac) in auto mode
xb@dnxb:~/Downloads$ sudo update-alternatives --config java
There are 3 choices for the alternative java (providing /usr/bin/java).

  Selection    Path                                            Priority   Status
------------------------------------------------------------
* 0            /usr/lib/jvm/jdk1.8.0_131/bin/java               20000     auto mode
  1            /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java   1081      manual mode
  2            /usr/lib/jvm/java-9-openjdk-amd64/bin/java       1091      manual mode
  3            /usr/lib/jvm/jdk1.8.0_131/bin/java               20000     manual mode

Press  to keep the current choice[*], or type selection number: 0
xb@dnxb:~/Downloads$ java -version #now it did changed to "1.8.0_131", originally is "9-Ubuntu"
java version "1.8.0_131"
Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)
xb@dnxb:~/Downloads$ java -jar ClassyShark.jar -open app-release.apk #now it should works, cheers :)
...
xb@dnxb:~/Downloads$ java -jar /opt/jd-gui/jd-gui-1.4.0.jar <Your class file> #is working too

[UPDATE]: I found this thread, he's right, if I select downgraded version
/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java on `sudo update-alternatives --config java` above, can solve the problem too.