"♦: command not found" in tty after login

18,408

Solution 1

Workaround

First, I think that you refer about when you go in tty1 - Ctrl + Alt + F1.

Now, I think is happening what you said most likely because you have a strange character like ♦ (diamond suit character or the special badge for an askubuntu moderator) in ~/.bashrc or ~/.profile file or other file that contain various initialization commands.

As you can see in next image, I edited ~/.bashrc file puting inside ♦ character on a single line. As result, when I open the terminal it get the problem described by you:

terminal

It is happening the same when I go in tty1 with Ctrl + Alt + F1.

Files that contain initialization commands when a shell is invoked: /etc/profile, /etc/bashrc, ~/.bash_login, ~/.profile, ~/.bashrc, ~/.bash_aliases and maybe others. See Shell initialization files.

To quickly check if one of these files have something wrong inside you can use source command. For example:

source ~/.bashrc

source profile

Final solution

After inspecting /etc/profile from http://paste.ubuntu.com/5781361/, I found that on the line 31 there is "Right-To-Left Override" - ‮ unicode character. Just open /etc/profile file with sudo -H gedit /etc/profile, make sure to delete this strange character and the problem will disappear.

profile file

As amusement, in HTML for example, if you insert this unicode character using decimal code (‮) in front of a line, look what is happening:

‮This text is in arabic-english!

Another more generalized solution

We will find the exact command that causing the error using a "trap".

First, we have to make a new script file in ~/bin directory, let's call it lib.trap.sh (gedit ~/bin/lib.trap.sh), with following inside:

lib_name='trap'

lib_version=20130620
#changed from lib_version=20121026 found it at https://stackoverflow.com/a/13099228/2353900 to work well at initialization of the shell

stderr_log="/dev/shm/stderr.log"

#
# TO BE SOURCED ONLY ONCE:
#
###~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##

if test "${g_libs[$lib_name]+_}"; then
    return 0
else
    if test ${#g_libs[@]} == 0; then
        declare -A g_libs
    fi
    g_libs[$lib_name]=$lib_version
fi


#
# MAIN CODE:
#
###~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##

set -o pipefail  # trace ERR through pipes
set -o errtrace  # trace ERR through 'time command' and other functions
set -o nounset   ## set -u : exit the script if you try to use an uninitialised variable
set -o errexit   ## set -e : exit the script if any statement returns a non-true return value

exec 2>"$stderr_log"


###~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
#
# FUNCTION: EXIT_HANDLER
#
###~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##

function exit_handler ()
{
    local error_code="$?"

    test $error_code == 0 && return;

    #
    # LOCAL VARIABLES:
    # ------------------------------------------------------------------
    #    
    local i=0
    local regex=''
    local mem=''

    local error_file=''
    local error_lineno=''
    local error_message='unknown'

    local lineno=''


    #
    # PRINT THE HEADER:
    # ------------------------------------------------------------------
    #
    # Color the output if it's an interactive terminal
    test -t 1 && tput bold; tput setf 4                                 ## red bold
    echo -e "\n(!) EXIT HANDLER\n"


    #
    # GETTING LAST ERROR OCCURRED:
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #

    #
    # Read last file from the error log
    # ------------------------------------------------------------------
    #
    if test -f "$stderr_log"
        then
            stderr=$( tail -n 1 "$stderr_log" )
            rm "$stderr_log"
    fi

    #
    # Managing the line to extract information:
    # ------------------------------------------------------------------
    #

    if test -n "$stderr"
        then        
            # Exploding stderr on :
            mem="$IFS"
            local shrunk_stderr=$( echo "$stderr" | sed 's/\: /\:/g' )
            IFS=':'
            local stderr_parts=( $shrunk_stderr )
            IFS="$mem"

            # Storing information on the error
            error_file="${stderr_parts[0]}"
            error_lineno="${stderr_parts[1]}"
            error_message=""

            for (( i = 3; i <= ${#stderr_parts[@]}; i++ ))
                do
                    error_message="$error_message "${stderr_parts[$i-1]}": "
            done

            # Removing last ':' (colon character)
            error_message="${error_message%:*}"

            # Trim
            error_message="$( echo "$error_message" | sed -e 's/^[ \t]*//' | sed -e 's/[ \t]*$//' )"
    fi

    #
    # GETTING BACKTRACE:
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
    _backtrace=$( backtrace 2 )


    #
    # MANAGING THE OUTPUT:
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #

    local lineno=""
    regex='^([a-z]{1,}) ([0-9]{1,})$'

    if [[ $error_lineno =~ $regex ]]

        # The error line was found on the log
        # (e.g. type 'ff' without quotes wherever)
        # --------------------------------------------------------------
        then
            local row="${BASH_REMATCH[1]}"
            lineno="${BASH_REMATCH[2]}"

            echo -e "FILE:\t\t${error_file}"
            echo -e "${row^^}:\t\t${lineno}\n"

            echo -e "ERROR CODE:\t${error_code}"             
            test -t 1 && tput setf 6                                    ## white yellow
            echo -e "ERROR MESSAGE:\n$error_message"


        else
            regex="^${error_file}\$|^${error_file}\s+|\s+${error_file}\s+|\s+${error_file}\$"
            if [[ "$_backtrace" =~ $regex ]]

                # The file was found on the log but not the error line
                # (could not reproduce this case so far)
                # ------------------------------------------------------
                then
                    echo -e "FILE:\t\t$error_file"
                    echo -e "ROW:\t\tunknown\n"

                    echo -e "ERROR CODE:\t${error_code}"
                    test -t 1 && tput setf 6                            ## white yellow
                    echo -e "ERROR MESSAGE:\n${stderr}"

                # Neither the error line nor the error file was found on the log
                # (e.g. type 'cp ffd fdf' without quotes wherever)
                # ------------------------------------------------------
                else
                    #
                    # The error file is the first on backtrace list:

                    # Exploding backtrace on newlines
                    mem=$IFS
                    IFS='
                    '
                    #
                    # Substring: I keep only the carriage return
                    # (others needed only for tabbing purpose)
                    IFS=${IFS:0:1}
                    local lines=( $_backtrace )

                    IFS=$mem

                    error_file=""

                    if test -n "${lines[1]}"
                        then
                            array=( ${lines[1]} )

                            for (( i=2; i<${#array[@]}; i++ ))
                                do
                                    error_file="$error_file ${array[$i]}"
                            done

                            # Trim
                            error_file="$( echo "$error_file" | sed -e 's/^[ \t]*//' | sed -e 's/[ \t]*$//' )"
                    fi
                    
            echo -e "ROW, FILE:\t\t${lines[2]   }\n"

                    echo -e "ERROR CODE:\t${error_code}"
                    test -t 1 && tput setf 6                            ## white yellow
                    if test -n "${stderr}"
                        then
                            echo -e "ERROR MESSAGE:\n${stderr}"
                        else
                            echo -e "ERROR MESSAGE:\n${error_message}"
                    fi
            fi
    fi

    #
    # PRINTING THE BACKTRACE:
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #

    test -t 1 && tput setf 7                                            ## white bold
    echo -e "\n$_backtrace\n"

    #
    # EXITING:
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #

    test -t 1 && tput setf 4                                            ## red bold
    echo "Exiting!"

    test -t 1 && tput sgr0 # Reset terminal

    exit "$error_code"
}
trap exit_handler ERR                                                  # ! ! ! TRAP EXIT ! ! !
#trap exit ERR                                                        # ! ! ! TRAP ERR ! ! ! 


###~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
#
# FUNCTION: BACKTRACE
#
###~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##

function backtrace
{
    local _start_from_=0

    local params=( "$@" )
    if (( "${#params[@]}" >= "1" ))
        then
            _start_from_="$1"
    fi

    local i=0
    local first=false
    while caller $i > /dev/null
    do
        if test -n "$_start_from_" && (( "$i" + 1   >= "$_start_from_" ))
            then
                if test "$first" == false
                    then
                        echo "BACKTRACE IS:"
                        first=true
                fi
                caller $i
        fi
        let "i=i+1"
    done
}

return 0

Now, the only thing what you have to do is to put next line at the begining of the file /etc/profile (sudo -H gedit /etc/profile):

source '/home/<user_name>/bin/lib.trap.sh'

Change <user_name> with your user name. Like this, all the files that contain initialization commands when a shell is invoked will pass through the "trap".

To test if there is a wrong command in /etc/profile for example, run in terminal next commands:

bash source /etc/profile

If something is wrong, like in this case, the result will be:

trap

So, now we know for sure that there is a problem (command not found) in /etc/profile file at line 32 (it is not at the line 31 as above because we have inserted a new line at the beginning of the file).

Many thanks to Luca Borrione for his script from this answer that helped me to complete this generalized solution.

Solution 2

For debugging the initialization scripts of bash, run the following (after having logged in at the virtual console).

PS4='+ $BASH_SOURCE:$LINENO:' bash -xlic ''

The above runs bash in interactive (-i) login (-l) mode, the same as the login program does when you log into a virtual console. -c '' makes it exit immediately after running through the initialization scripts, and the -x and PS4=... makes it output each command, before it runs them, along with the filename and line number of that command. That should help determining which line of which file that invalid command resides.

On a side note, ♦ is the symbol the default font for the virtual console uses to print characters it does not have a symbol for.

Solution 3

While searching your initialization files, it may be helpful to look for the hexadecimal used for outputting the ♦. The hex code for ♦ is 2666, according to Unicode Character 'BLACK DIAMOND SUIT' . Note: There is at least one other hex code, 25C6, which produces the same or similar looking symbol. See the search results for "diamond". Unicode Character Search

Perhaps something like \u2666 is in one of the scripts. From Bash Reference Manual for echo - "\uhhhh the Unicode (ISO/IEC 10646) character whose value is the hexadecimal value HHHH (one to four hex digits)"

It depends on the character encoding used, so you may want to search the most likely ones first. echo $LC_CTYPE should return the character encoding used by your shell. See How to get terminal's Character Encoding

Share:
18,408

Related videos on Youtube

Vitaly Zdanevich
Author by

Vitaly Zdanevich

Updated on September 18, 2022

Comments

  • Vitaly Zdanevich
    Vitaly Zdanevich over 1 year

    I have this problem after upgrade Lubuntu from 12.10 to 13.04.

    I press Ctrl + Alt + 1, enter login, password, wait two seconds and get: ♦: command not found". After this message I can type commands without problems, but what is it?

    echo $PATH
    /usr/lib/lightdm/lightdm:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/home/vitaly/bin:/usr/java/jdk1.7.0_17/bin
    

    My .bashrc file is:

    # ~/.bashrc: executed by bash(1) for non-login shells.
    # see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)
    # for examples
    
    # If not running interactively, don't do anything
    case $- in
        *i*) ;;
          *) return;;
    esac
    
    # don't put duplicate lines or lines starting with space in the history.
    # See bash(1) for more options
    HISTCONTROL=ignoreboth
    
    # append to the history file, don't overwrite it
    shopt -s histappend
    
    # for setting history length see HISTSIZE and HISTFILESIZE in bash(1)
    HISTSIZE=1000
    HISTFILESIZE=2000
    
    # check the window size after each command and, if necessary,
    # update the values of LINES and COLUMNS.
    shopt -s checkwinsize
    
    # If set, the pattern "**" used in a pathname expansion context will
    # match all files and zero or more directories and subdirectories.
    #shopt -s globstar
    
    # make less more friendly for non-text input files, see lesspipe(1)
    [ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)"
    
    # set variable identifying the chroot you work in (used in the prompt below)
    if [ -z "${debian_chroot:-}" ] && [ -r /etc/debian_chroot ]; then
        debian_chroot=$(cat /etc/debian_chroot)
    fi
    
    # set a fancy prompt (non-color, unless we know we "want" color)
    case "$TERM" in
        xterm-color) color_prompt=yes;;
    esac
    
    # uncomment for a colored prompt, if the terminal has the capability; turned
    # off by default to not distract the user: the focus in a terminal window
    # should be on the output of commands, not on the prompt
    #force_color_prompt=yes
    
    if [ -n "$force_color_prompt" ]; then
        if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then
        # We have color support; assume it's compliant with Ecma-48
        # (ISO/IEC-6429). (Lack of such support is extremely rare, and such
        # a case would tend to support setf rather than setaf.)
        color_prompt=yes
        else
        color_prompt=
        fi
    fi
    
    if [ "$color_prompt" = yes ]; then
        PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
    else
        PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
    fi
    unset color_prompt force_color_prompt
    
    # If this is an xterm set the title to user@host:dir
    case "$TERM" in
    xterm*|rxvt*)
        PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1"
        ;;
    *)
        ;;
    esac
    
    # enable color support of ls and also add handy aliases
    if [ -x /usr/bin/dircolors ]; then
        test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)"
        alias ls='ls --color=auto'
        #alias dir='dir --color=auto'
        #alias vdir='vdir --color=auto'
    
        alias grep='grep --color=auto'
        alias fgrep='fgrep --color=auto'
        alias egrep='egrep --color=auto'
    fi
    
    # some more ls aliases
    alias ll='ls -alF'
    alias la='ls -A'
    alias l='ls -CF'
    
    # Add an "alert" alias for long running commands.  Use like so:
    #   sleep 10; alert
    alias alert='notify-send --urgency=low -i "$([ $? = 0 ] && echo terminal || echo error)" "$(history|tail -n1|sed -e '\''s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//'\'')"'
    
    # Alias definitions.
    # You may want to put all your additions into a separate file like
    # ~/.bash_aliases, instead of adding them here directly.
    # See /usr/share/doc/bash-doc/examples in the bash-doc package.
    
    if [ -f ~/.bash_aliases ]; then
        . ~/.bash_aliases
    fi
    
    # enable programmable completion features (you don't need to enable
    # this, if it's already enabled in /etc/bash.bashrc and /etc/profile
    # sources /etc/bash.bashrc).
    if ! shopt -oq posix; then
      if [ -f /usr/share/bash-completion/bash_completion ]; then
        . /usr/share/bash-completion/bash_completion
      elif [ -f /etc/bash_completion ]; then
        . /etc/bash_completion
      fi
    fi
    

    My .profile file is:

    # ~/.profile: executed by the command interpreter for login shells.
    # This file is not read by bash(1), if ~/.bash_profile or ~/.bash_login
    # exists.
    # see /usr/share/doc/bash/examples/startup-files for examples.
    # the files are located in the bash-doc package.
    
    # the default umask is set in /etc/profile; for setting the umask
    # for ssh logins, install and configure the libpam-umask package.
    #umask 022
    
    # if running bash
    if [ -n "$BASH_VERSION" ]; then
        # include .bashrc if it exists
        if [ -f "$HOME/.bashrc" ]; then
        . "$HOME/.bashrc"
        fi
    fi
    
    # set PATH so it includes user's private bin if it exists
    if [ -d "$HOME/bin" ] ; then
        PATH="$HOME/bin:$PATH"
    fi
    

    The file /etc/profile is here: http://paste.ubuntu.com/5781361/

  • Vitaly Zdanevich
    Vitaly Zdanevich almost 11 years
    Added code from .bashrc and .profile to question - I can't find ♦ here.
  • Radu Rădeanu
    Radu Rădeanu almost 11 years
    @VitalyZdanevich You should check all the files that contain initialization commands (.bash_aliases, .pam_environment, etc) for somenthing strange inside, not necessarily exactly this character.
  • Radu Rădeanu
    Radu Rădeanu almost 11 years
    @VitalyZdanevich Check also /etc/profile and /etc/bashrc files. Also, I edited my answer.
  • Radu Rădeanu
    Radu Rădeanu almost 11 years
    -1 because in ~/.bash_history are stored the commands runned interactive on PS1.
  • iyrin
    iyrin almost 11 years
    Thank you for confirming. I have removed it. Should I have added the rest as a comment to your answer, Radu?
  • Vitaly Zdanevich
    Vitaly Zdanevich almost 11 years
    Oh, I don't understand - what I need to do? Where in Lubuntu initialization files? I tried full-text search for \u2666 and ♦ in Catfish (Lubuntu search) - nothing. I sow history - nothing. I see this message only in tty only after login. After echo $LC_CTYPE I get empty line.
  • Vitaly Zdanevich
    Vitaly Zdanevich almost 11 years
    After source /etc/profile I get : command not found. My `/etc/profile here - paste.ubuntu.com/5781361
  • Radu Rădeanu
    Radu Rădeanu almost 11 years
    Yes, the problem is on the line 31 from /etc/profile. You have something very strange there. Just delete everithing betwen fi and JAVA_HOME, after press one or two 'Enter' and everything should be ok after. Modify the file with sudo gedit /etc/profile
  • iyrin
    iyrin almost 11 years
    Where do you see this in his /etc/profile? I don't see the rlo in the pastebin or its html source.
  • Radu Rădeanu
    Radu Rădeanu almost 11 years
    @RyanLoremIpsum In Chromium, select line 31 from paste.ubuntu.com/5781361, right click and select 'Inspect Element'. You will get the result from my last image from the answer.
  • iyrin
    iyrin almost 11 years
    Got it! Thanks. Do you suspect that the rlo is causing his shell to output the diamond be reading something from right to left?
  • iyrin
    iyrin almost 11 years
    Running locale should show the LC_CTYPE. locale
  • iyrin
    iyrin almost 11 years
    Try Radu's answer before this one! If we narrow down the character set used in your tty, you can search for the occurrence of the corresponding character code for the solid diamond. This search would prove to be fruitless if Radu's RLO find is cause tho.
  • Radu Rădeanu
    Radu Rădeanu almost 11 years
    @RyanLoremIpsum No, I'm sure. The OP had pasted there, not me :)
  • Vitaly Zdanevich
    Vitaly Zdanevich almost 11 years
    Great! I clear this two transparent lines in /etc/profile. Strange bag.
  • iyrin
    iyrin almost 11 years
    Perhaps it appeared as transparent in your editor because RLO shouldn't output a character. Anyway, that was Radu's bounty so pay up. :)