as linhas do histórico da libreadline combinam

1

Isso tem me deixado louco por cerca de três anos. Eu não sei como descrever completamente o problema, mas acho que posso finalmente descrever uma maneira de recriá-lo. Sua milhagem pode variar. Eu tenho uma mistura de máquinas Ubuntu e desktop de várias versões e algumas máquinas gentoo com vários estados de ruína. Todos parecem fazer suas próprias coisas, embora com semelhanças.

Tente isso e deixe-me saber se você vê a mesma coisa.

  1. pop abrir dois xterms (TERM = xterm)
  2. redimensione uma para que não sejam as mesmas
  3. edite a tela -R test1 em um (TERM = tela)
  4. e tela -x test1 no outro
  5. hooray, digitar um aparece no outro; embora note que seu tamanho diferente produz artefatos e coisas
  6. emitem alguns comandos no seu shell
  7. aperte ^ AF no que não se encaixa bem, agora cabe !!
  8. rola um pouco sobre a história
  9. goto 6

Eventualmente, você notará algumas linhas de histórico combinadas. Se você não fizer isso, então é algo exclusivo para minha configuração, que abrange várias distribuições e computadores; então isso é um conceito confuso para mim.

Se você vê a coisa que estou vendo, então:

bash$ ls -al
bash$ ps auxfw

torna-se isto:

bash$ ls -al; ps auxfw

Isso não acontece todas as vezes. Eu tenho que realmente brincar com isso - a menos que eu não queira que isso aconteça, então sempre acontece. Em alguns sistemas (ou combinações), recebo um separador de linha como o exemplo acima. Em alguns sistemas, não sei. O fato de eu obter o separador de linhas em alguns sistemas parece indicar para mim que o bash suporta esse comportamento. Sua história é inteiramente manipulada pela libreadline e depois de examinar (isto é, ler atentamente) as man pages, não consegui encontrar uma única configuração de readline para combinar duas linhas de histórico. Nem posso encontrar nada na manchagem bash.

Então, como posso invocar isso de propósito? Ou, se não posso fazer isso, como posso desativá-lo completamente? Eu tomaria uma das duas respostas como solução. Atualmente, só vejo quando não quero.

    
por jettero 29.01.2011 / 01:37

1 resposta

0

Do CHANGELOG para o bash-4.2-rc2:

This document details the changes between this version, bash-4.2-alpha, and the previous version, bash-4.1-release.

q. Fixed a bug that caused spurious semicolons to be added into the command history in certain cases.

O tarball do release candidate está disponível aqui .

Editar:

Aqui estão alguns diffs parciais entre o Bash 3.2 e o Bash 4.2 RC2:

y.tab.c:

#if defined (HISTORY)                                                                                                                   #if defined (HISTORY)
/* A list of tokens which can be followed by newlines, but not by                                                                       /* A list of tokens which can be followed by newlines, but not by
   semi-colons.  When concatenating multiple lines of history, the                                                                         semi-colons.  When concatenating multiple lines of history, the
   newline separator for such tokens is replaced with a space. */                                                                          newline separator for such tokens is replaced with a space. */
static int no_semi_successors[] = {                                                                                                  |  static const int no_semi_successors[] = {
  '\n', '{', '(', ')', ';', '&', '|',                                                                                                     '\n', '{', '(', ')', ';', '&', '|',
  CASE, DO, ELSE, IF, SEMI_SEMI, THEN, UNTIL, WHILE, AND_AND, OR_OR, IN,                                                             |    CASE, DO, ELSE, IF, SEMI_SEMI, SEMI_AND, SEMI_SEMI_AND, THEN, UNTIL,
                                                                                                                                     >    WHILE, AND_AND, OR_OR, IN,
  0                                                                                                                                       0
};                                                                                                                                      };

/* If we are not within a delimited expression, try to be smart                                                                         /* If we are not within a delimited expression, try to be smart
   about which separators can be semi-colons and which must be                                                                             about which separators can be semi-colons and which must be
   newlines.  Returns the string that should be added into the                                                                             newlines.  Returns the string that should be added into the
   history entry. */                                                                                                                 |     history entry.  LINE is the line we're about to add; it helps
                                                                                                                                     >     make some more intelligent decisions in certain cases. */
char *                                                                                                                                  char *
history_delimiting_chars ()                                                                                                          |  history_delimiting_chars (line)
                                                                                                                                     >       const char *line;
{                                                                                                                                       {
                                                                                                                                     >    static int last_was_heredoc = 0;      /* was the last entry the start of a here document? */
  register int i;                                                                                                                         register int i;

                                                                                                                                     >    if ((parser_state & PST_HEREDOC) == 0)
                                                                                                                                     >      last_was_heredoc = 0;
                                                                                                                                     >
  if (dstack.delimiter_depth != 0)                                                                                                        if (dstack.delimiter_depth != 0)
    return ("\n");                                                                                                                          return ("\n");
                                                                                                                                     |
                                                                                                                                     >    /* We look for current_command_line_count == 2 because we are looking to
                                                                                                                                     >       add the first line of the body of the here document (the second line
                                                                                                                                     >       of the command).  We also keep LAST_WAS_HEREDOC as a private sentinel
                                                                                                                                     >       variable to note when we think we added the first line of a here doc
                                                                                                                                     >       (the one with a "<<" somewhere in it) */
                                                                                                                                     >    if (parser_state & PST_HEREDOC)
                                                                                                                                     >      {
                                                                                                                                     >        if (last_was_heredoc)
                                                                                                                                     >          {
                                                                                                                                     >            last_was_heredoc = 0;
                                                                                                                                     >            return "\n";
                                                                                                                                     >          }
                                                                                                                                     >        return (current_command_line_count == 2 ? "\n" : "");
                                                                                                                                     >      }
                                                                                                                                     >
  /* First, handle some special cases. */                                                                                                 /* First, handle some special cases. */
  /*(*/                                                                                                                                   /*(*/
  /* If we just read '()', assume it's a function definition, and don't                                                                   /* If we just read '()', assume it's a function definition, and don't
     add a semicolon.  If the token before the ')' was not '(', and we're                                                                    add a semicolon.  If the token before the ')' was not '(', and we're
     not in the midst of parsing a case statement, assume it's a                                                                             not in the midst of parsing a case statement, assume it's a
     parenthesized command and add the semicolon. */                                                                                         parenthesized command and add the semicolon. */
  /*)(*/                                                                                                                                  /*)(*/
  if (token_before_that == ')')                                                                                                           if (token_before_that == ')')
    {                                                                                                                                       {
      if (two_tokens_ago == '(')        /*)*/   /* function def */                                                                            if (two_tokens_ago == '(')        /*)*/   /* function def */
        return " ";                                                                                                                             return " ";
      /* This does not work for subshells inside case statement                                                                               /* This does not work for subshells inside case statement
         command lists.  It's a suboptimal solution. */                                                                                          command lists.  It's a suboptimal solution. */
      else if (parser_state & PST_CASESTMT)     /* case statement pattern */                                                                  else if (parser_state & PST_CASESTMT)     /* case statement pattern */
        return " ";                                                                                                                             return " ";
      else                                                                                                                                    else
        return "; ";                            /* (...) subshell */                                                                            return "; ";                            /* (...) subshell */
    }                                                                                                                                       }
  else if (token_before_that == WORD && two_tokens_ago == FUNCTION)                                                                       else if (token_before_that == WORD && two_tokens_ago == FUNCTION)
    return " ";         /* function def using 'function name' without '()' */                                                               return " ";         /* function def using 'function name' without '()' */

                                                                                                                                     >    /* If we're not in a here document, but we think we're about to parse one,
                                                                                                                                     >       and we would otherwise return a ';', return a newline to delimit the
                                                                                                                                     >       line with the here-doc delimiter */
                                                                                                                                     >    else if ((parser_state & PST_HEREDOC) == 0 && current_command_line_count > 1 && last_read_token == '\n' && strstr (line, "<<"))
                                                                                                                                     >      {
                                                                                                                                     >        last_was_heredoc = 1;
                                                                                                                                     >        return "\n";
                                                                                                                                     >      }
                                                                                                                                     >
  else if (token_before_that == WORD && two_tokens_ago == FOR)                                                                            else if (token_before_that == WORD && two_tokens_ago == FOR)
    {                                                                                                                                       {
      /* Tricky.  'for i\nin ...' should not have a semicolon, but                                                                            /* Tricky.  'for i\nin ...' should not have a semicolon, but
         'for i\ndo ...' should.  We do what we can. */                                                                                          'for i\ndo ...' should.  We do what we can. */
      for (i = shell_input_line_index; whitespace(shell_input_line[i]); i++)                                                         |        for (i = shell_input_line_index; whitespace (shell_input_line[i]); i++)
        ;                                                                                                                                       ;
      if (shell_input_line[i] && shell_input_line[i] == 'i' && shell_input_line[i+1] == 'n')                                                  if (shell_input_line[i] && shell_input_line[i] == 'i' && shell_input_line[i+1] == 'n')
        return " ";                                                                                                                             return " ";
      return ";";                                                                                                                             return ";";
    }                                                                                                                                       }
  else if (two_tokens_ago == CASE && token_before_that == WORD && (parser_state & PST_CASESTMT))                                          else if (two_tokens_ago == CASE && token_before_that == WORD && (parser_state & PST_CASESTMT))
    return " ";                                                                                                                             return " ";

  for (i = 0; no_semi_successors[i]; i++)                                                                                                 for (i = 0; no_semi_successors[i]; i++)
    {                                                                                                                                       {
      if (token_before_that == no_semi_successors[i])                                                                                         if (token_before_that == no_semi_successors[i])
        return (" ");                                                                                                                           return (" ");
    }                                                                                                                                       }

  return ("; ");                                                                                                                          return ("; ");
}                                                                                                                                       }
#endif /* HISTORY */                                                                                                                    #endif /* HISTORY */

bashhist.c:

/* Add a line to the history list.                                                                                                      /* Add a line to the history list.
   The variable COMMAND_ORIENTED_HISTORY controls the style of history                                                                     The variable COMMAND_ORIENTED_HISTORY controls the style of history
   remembering;  when non-zero, and LINE is not the first line of a                                                                        remembering;  when non-zero, and LINE is not the first line of a
   complete parser construct, append LINE to the last history line instead                                                                 complete parser construct, append LINE to the last history line instead
   of adding it as a new line. */                                                                                                          of adding it as a new line. */
void                                                                                                                                    void
bash_add_history (line)                                                                                                                 bash_add_history (line)
     char *line;                                                                                                                             char *line;
{                                                                                                                                       {
  int add_it, offset, curlen;                                                                                                             int add_it, offset, curlen;
  HIST_ENTRY *current, *old;                                                                                                              HIST_ENTRY *current, *old;
  char *chars_to_add, *new_line;                                                                                                          char *chars_to_add, *new_line;

  add_it = 1;                                                                                                                             add_it = 1;
  if (command_oriented_history && current_command_line_count > 1)                                                                         if (command_oriented_history && current_command_line_count > 1)
    {                                                                                                                                       {
      chars_to_add = literal_history ? "\n" : history_delimiting_chars ();                                                           |        chars_to_add = literal_history ? "\n" : history_delimiting_chars (line);

      using_history ();                                                                                                                       using_history ();
      current = previous_history ();                                                                                                          current = previous_history ();

      if (current)                                                                                                                            if (current)
        {                                                                                                                                       {
          /* If the previous line ended with an escaped newline (escaped                                                                          /* If the previous line ended with an escaped newline (escaped
             with backslash, but otherwise unquoted), then remove the quoted                                                                         with backslash, but otherwise unquoted), then remove the quoted
             newline, since that is what happens when the line is parsed. */                                                                         newline, since that is what happens when the line is parsed. */
          curlen = strlen (current->line);                                                                                                        curlen = strlen (current->line);

          if (dstack.delimiter_depth == 0 && current->line[curlen - 1] == '\' &&                                                                 if (dstack.delimiter_depth == 0 && current->line[curlen - 1] == '\' &&
              current->line[curlen - 2] != '\')                                                                                                      current->line[curlen - 2] != '\')
            {                                                                                                                                       {
              current->line[curlen - 1] = '
#if defined (HISTORY)                                                                                                                   #if defined (HISTORY)
/* A list of tokens which can be followed by newlines, but not by                                                                       /* A list of tokens which can be followed by newlines, but not by
   semi-colons.  When concatenating multiple lines of history, the                                                                         semi-colons.  When concatenating multiple lines of history, the
   newline separator for such tokens is replaced with a space. */                                                                          newline separator for such tokens is replaced with a space. */
static int no_semi_successors[] = {                                                                                                  |  static const int no_semi_successors[] = {
  '\n', '{', '(', ')', ';', '&', '|',                                                                                                     '\n', '{', '(', ')', ';', '&', '|',
  CASE, DO, ELSE, IF, SEMI_SEMI, THEN, UNTIL, WHILE, AND_AND, OR_OR, IN,                                                             |    CASE, DO, ELSE, IF, SEMI_SEMI, SEMI_AND, SEMI_SEMI_AND, THEN, UNTIL,
                                                                                                                                     >    WHILE, AND_AND, OR_OR, IN,
  0                                                                                                                                       0
};                                                                                                                                      };

/* If we are not within a delimited expression, try to be smart                                                                         /* If we are not within a delimited expression, try to be smart
   about which separators can be semi-colons and which must be                                                                             about which separators can be semi-colons and which must be
   newlines.  Returns the string that should be added into the                                                                             newlines.  Returns the string that should be added into the
   history entry. */                                                                                                                 |     history entry.  LINE is the line we're about to add; it helps
                                                                                                                                     >     make some more intelligent decisions in certain cases. */
char *                                                                                                                                  char *
history_delimiting_chars ()                                                                                                          |  history_delimiting_chars (line)
                                                                                                                                     >       const char *line;
{                                                                                                                                       {
                                                                                                                                     >    static int last_was_heredoc = 0;      /* was the last entry the start of a here document? */
  register int i;                                                                                                                         register int i;

                                                                                                                                     >    if ((parser_state & PST_HEREDOC) == 0)
                                                                                                                                     >      last_was_heredoc = 0;
                                                                                                                                     >
  if (dstack.delimiter_depth != 0)                                                                                                        if (dstack.delimiter_depth != 0)
    return ("\n");                                                                                                                          return ("\n");
                                                                                                                                     |
                                                                                                                                     >    /* We look for current_command_line_count == 2 because we are looking to
                                                                                                                                     >       add the first line of the body of the here document (the second line
                                                                                                                                     >       of the command).  We also keep LAST_WAS_HEREDOC as a private sentinel
                                                                                                                                     >       variable to note when we think we added the first line of a here doc
                                                                                                                                     >       (the one with a "<<" somewhere in it) */
                                                                                                                                     >    if (parser_state & PST_HEREDOC)
                                                                                                                                     >      {
                                                                                                                                     >        if (last_was_heredoc)
                                                                                                                                     >          {
                                                                                                                                     >            last_was_heredoc = 0;
                                                                                                                                     >            return "\n";
                                                                                                                                     >          }
                                                                                                                                     >        return (current_command_line_count == 2 ? "\n" : "");
                                                                                                                                     >      }
                                                                                                                                     >
  /* First, handle some special cases. */                                                                                                 /* First, handle some special cases. */
  /*(*/                                                                                                                                   /*(*/
  /* If we just read '()', assume it's a function definition, and don't                                                                   /* If we just read '()', assume it's a function definition, and don't
     add a semicolon.  If the token before the ')' was not '(', and we're                                                                    add a semicolon.  If the token before the ')' was not '(', and we're
     not in the midst of parsing a case statement, assume it's a                                                                             not in the midst of parsing a case statement, assume it's a
     parenthesized command and add the semicolon. */                                                                                         parenthesized command and add the semicolon. */
  /*)(*/                                                                                                                                  /*)(*/
  if (token_before_that == ')')                                                                                                           if (token_before_that == ')')
    {                                                                                                                                       {
      if (two_tokens_ago == '(')        /*)*/   /* function def */                                                                            if (two_tokens_ago == '(')        /*)*/   /* function def */
        return " ";                                                                                                                             return " ";
      /* This does not work for subshells inside case statement                                                                               /* This does not work for subshells inside case statement
         command lists.  It's a suboptimal solution. */                                                                                          command lists.  It's a suboptimal solution. */
      else if (parser_state & PST_CASESTMT)     /* case statement pattern */                                                                  else if (parser_state & PST_CASESTMT)     /* case statement pattern */
        return " ";                                                                                                                             return " ";
      else                                                                                                                                    else
        return "; ";                            /* (...) subshell */                                                                            return "; ";                            /* (...) subshell */
    }                                                                                                                                       }
  else if (token_before_that == WORD && two_tokens_ago == FUNCTION)                                                                       else if (token_before_that == WORD && two_tokens_ago == FUNCTION)
    return " ";         /* function def using 'function name' without '()' */                                                               return " ";         /* function def using 'function name' without '()' */

                                                                                                                                     >    /* If we're not in a here document, but we think we're about to parse one,
                                                                                                                                     >       and we would otherwise return a ';', return a newline to delimit the
                                                                                                                                     >       line with the here-doc delimiter */
                                                                                                                                     >    else if ((parser_state & PST_HEREDOC) == 0 && current_command_line_count > 1 && last_read_token == '\n' && strstr (line, "<<"))
                                                                                                                                     >      {
                                                                                                                                     >        last_was_heredoc = 1;
                                                                                                                                     >        return "\n";
                                                                                                                                     >      }
                                                                                                                                     >
  else if (token_before_that == WORD && two_tokens_ago == FOR)                                                                            else if (token_before_that == WORD && two_tokens_ago == FOR)
    {                                                                                                                                       {
      /* Tricky.  'for i\nin ...' should not have a semicolon, but                                                                            /* Tricky.  'for i\nin ...' should not have a semicolon, but
         'for i\ndo ...' should.  We do what we can. */                                                                                          'for i\ndo ...' should.  We do what we can. */
      for (i = shell_input_line_index; whitespace(shell_input_line[i]); i++)                                                         |        for (i = shell_input_line_index; whitespace (shell_input_line[i]); i++)
        ;                                                                                                                                       ;
      if (shell_input_line[i] && shell_input_line[i] == 'i' && shell_input_line[i+1] == 'n')                                                  if (shell_input_line[i] && shell_input_line[i] == 'i' && shell_input_line[i+1] == 'n')
        return " ";                                                                                                                             return " ";
      return ";";                                                                                                                             return ";";
    }                                                                                                                                       }
  else if (two_tokens_ago == CASE && token_before_that == WORD && (parser_state & PST_CASESTMT))                                          else if (two_tokens_ago == CASE && token_before_that == WORD && (parser_state & PST_CASESTMT))
    return " ";                                                                                                                             return " ";

  for (i = 0; no_semi_successors[i]; i++)                                                                                                 for (i = 0; no_semi_successors[i]; i++)
    {                                                                                                                                       {
      if (token_before_that == no_semi_successors[i])                                                                                         if (token_before_that == no_semi_successors[i])
        return (" ");                                                                                                                           return (" ");
    }                                                                                                                                       }

  return ("; ");                                                                                                                          return ("; ");
}                                                                                                                                       }
#endif /* HISTORY */                                                                                                                    #endif /* HISTORY */
'; current->line[curlen - 1] = '
/* Add a line to the history list.                                                                                                      /* Add a line to the history list.
   The variable COMMAND_ORIENTED_HISTORY controls the style of history                                                                     The variable COMMAND_ORIENTED_HISTORY controls the style of history
   remembering;  when non-zero, and LINE is not the first line of a                                                                        remembering;  when non-zero, and LINE is not the first line of a
   complete parser construct, append LINE to the last history line instead                                                                 complete parser construct, append LINE to the last history line instead
   of adding it as a new line. */                                                                                                          of adding it as a new line. */
void                                                                                                                                    void
bash_add_history (line)                                                                                                                 bash_add_history (line)
     char *line;                                                                                                                             char *line;
{                                                                                                                                       {
  int add_it, offset, curlen;                                                                                                             int add_it, offset, curlen;
  HIST_ENTRY *current, *old;                                                                                                              HIST_ENTRY *current, *old;
  char *chars_to_add, *new_line;                                                                                                          char *chars_to_add, *new_line;

  add_it = 1;                                                                                                                             add_it = 1;
  if (command_oriented_history && current_command_line_count > 1)                                                                         if (command_oriented_history && current_command_line_count > 1)
    {                                                                                                                                       {
      chars_to_add = literal_history ? "\n" : history_delimiting_chars ();                                                           |        chars_to_add = literal_history ? "\n" : history_delimiting_chars (line);

      using_history ();                                                                                                                       using_history ();
      current = previous_history ();                                                                                                          current = previous_history ();

      if (current)                                                                                                                            if (current)
        {                                                                                                                                       {
          /* If the previous line ended with an escaped newline (escaped                                                                          /* If the previous line ended with an escaped newline (escaped
             with backslash, but otherwise unquoted), then remove the quoted                                                                         with backslash, but otherwise unquoted), then remove the quoted
             newline, since that is what happens when the line is parsed. */                                                                         newline, since that is what happens when the line is parsed. */
          curlen = strlen (current->line);                                                                                                        curlen = strlen (current->line);

          if (dstack.delimiter_depth == 0 && current->line[curlen - 1] == '\' &&                                                                 if (dstack.delimiter_depth == 0 && current->line[curlen - 1] == '\' &&
              current->line[curlen - 2] != '\')                                                                                                      current->line[curlen - 2] != '\')
            {                                                                                                                                       {
              current->line[curlen - 1] = '%pre%';                                                                                                       current->line[curlen - 1] = '%pre%';
              curlen--;                                                                                                                               curlen--;
              chars_to_add = "";                                                                                                                      chars_to_add = "";
            }                                                                                                                                       }

                                                                                                                                     >            /* If we're not in some kind of quoted construct, the current history
                                                                                                                                     >               entry ends with a newline, and we're going to add a semicolon,
                                                                                                                                     >               don't.  In some cases, it results in a syntax error (e.g., before
                                                                                                                                     >               a close brace), and it should not be needed. */
                                                                                                                                     >            if (dstack.delimiter_depth == 0 && current->line[curlen - 1] == '\n' && *chars_to_add == ';')
                                                                                                                                     >              chars_to_add++;
                                                                                                                                     >
          new_line = (char *)xmalloc (1                                                                                                           new_line = (char *)xmalloc (1
                                      + curlen                                                                                                                                + curlen
                                      + strlen (line)                                                                                                                         + strlen (line)
                                      + strlen (chars_to_add));                                                                                                               + strlen (chars_to_add));
          sprintf (new_line, "%s%s%s", current->line, chars_to_add, line);                                                                        sprintf (new_line, "%s%s%s", current->line, chars_to_add, line);
          offset = where_history ();                                                                                                              offset = where_history ();
          old = replace_history_entry (offset, new_line, current->data);                                                                          old = replace_history_entry (offset, new_line, current->data);
          free (new_line);                                                                                                                        free (new_line);

          if (old)                                                                                                                                if (old)
            free_history_entry (old);                                                                                                               free_history_entry (old);

          add_it = 0;                                                                                                                             add_it = 0;
        }                                                                                                                                       }
    }                                                                                                                                       }

  if (add_it)                                                                                                                             if (add_it)
    really_add_history (line);                                                                                                              really_add_history (line);

                                                                                                                                     >  #if defined (SYSLOG_HISTORY)
                                                                                                                                     >    bash_syslog_history (line);
                                                                                                                                     >  #endif
                                                                                                                                     >
  using_history ();                                                                                                                       using_history ();
}                                                                                                                                       }
'; curlen--; curlen--; chars_to_add = ""; chars_to_add = ""; } } > /* If we're not in some kind of quoted construct, the current history > entry ends with a newline, and we're going to add a semicolon, > don't. In some cases, it results in a syntax error (e.g., before > a close brace), and it should not be needed. */ > if (dstack.delimiter_depth == 0 && current->line[curlen - 1] == '\n' && *chars_to_add == ';') > chars_to_add++; > new_line = (char *)xmalloc (1 new_line = (char *)xmalloc (1 + curlen + curlen + strlen (line) + strlen (line) + strlen (chars_to_add)); + strlen (chars_to_add)); sprintf (new_line, "%s%s%s", current->line, chars_to_add, line); sprintf (new_line, "%s%s%s", current->line, chars_to_add, line); offset = where_history (); offset = where_history (); old = replace_history_entry (offset, new_line, current->data); old = replace_history_entry (offset, new_line, current->data); free (new_line); free (new_line); if (old) if (old) free_history_entry (old); free_history_entry (old); add_it = 0; add_it = 0; } } } } if (add_it) if (add_it) really_add_history (line); really_add_history (line); > #if defined (SYSLOG_HISTORY) > bash_syslog_history (line); > #endif > using_history (); using_history (); } }
    
por 02.02.2011 / 16:03