Bash 中 SHLVL 變數為 1000 的時候

紫雲飛發表於2015-09-12

SHLVL 環境變數代表 Shell 巢狀執行的深度。

$ echo $SHLVL

1

$ bash

$ echo $SHLVL

2

$ bash

$ echo $SHLVL

3

在 Bash 裡,這個值的範圍是 [0, 1000],0 是怎麼來的呢?是在上次的 SHLVL 值為負數的時候:

$ SHLVL=-1

$ bash

$ echo $SHLVL

0

當 SHLVL 的值超過 1000 時,會自動重置到 1。

$ SHLVL=1000

$ bash

bash: warning: shell level (1001) too high, resetting to 1

$ echo $SHLVL

1

我也本以為就是這樣了,然而卻發現個特例:

$ SHLVL=999

$ bash

$ echo $SHLVL

 

在巢狀 1000 層的時候,SHEVL 看起來是個空字串,但在 Shell 裡很多東西眼見都不為實,所以我們用 16 進位制看看:

$ SHLVL=999 bash -c 'echo -n "$SHLVL" | hexdump'

0b 01

$ SHLVL=999 bash -c 'echo -n "$SHLVL" | hexdump'

0f 01

$ SHLVL=999 bash -c 'echo -n "$SHLVL" | hexdump'

04 01 

這個值原來是個隨機字串,在我的 Mac 上是兩個控制字元(不可見字元),所以看起來像是空字串。而在另外一臺 Red Hat 上執行了下是一些肉眼可見的亂碼:

$ SHLVL=999 bash -c 'echo "$SHLVL"'

�[g�

總之,也就是說 SHEVL 實際的值會在 [0, 999] 以及這個不確定的值這 1001 個值裡輪詢,不會真正到達 1000。

看了下 Bash 原始碼:

void
adjust_shell_level (change)
     int change;
{
  char new_level[5], *old_SHLVL;
  intmax_t old_level;
  SHELL_VAR *temp_var;

  old_SHLVL = get_string_value ("SHLVL");
  if (old_SHLVL == 0 || *old_SHLVL == '\0' || legal_number (old_SHLVL, &old_level) == 0)
    old_level = 0;

  shell_level = old_level + change;
  if (shell_level < 0)
    shell_level = 0;
  else if (shell_level > 1000)
    {
      internal_warning (_("shell level (%d) too high, resetting to 1"), shell_level);
      shell_level = 1;
    }

  /* We don't need the full generality of itos here. */
  if (shell_level < 10)
    {
      new_level[0] = shell_level + '0';
      new_level[1] = '\0';
    }
  else if (shell_level < 100)
    {
      new_level[0] = (shell_level / 10) + '0';
      new_level[1] = (shell_level % 10) + '0';
      new_level[2] = '\0';
    }
  else if (shell_level < 1000)
    {
      new_level[0] = (shell_level / 100) + '0';
      old_level = shell_level % 100;
      new_level[1] = (old_level / 10) + '0';
      new_level[2] = (old_level % 10) + '0';
      new_level[3] = '\0';
    }

  temp_var = bind_variable ("SHLVL", new_level, 0);
  set_auto_export (temp_var);
}

static void
initialize_shell_level ()
{
  adjust_shell_level (1);
}

它把數字轉字串的邏輯只寫到 shell_level < 1000 的地方,漏掉了最後一個 else 的樣子,導致 SHLVL 的值成為了一個未初始化的字串。我在 bug-bash 上發了郵件,這個 bug 會在 Bash 4.4 中修復 http://lists.gnu.org/archive/html/bug-bash/2015-09/msg00057.html。順便說一句,ksh 和 zsh 都沒有這個 1000 限制。

相關文章