When running a shell or most programs in a shell anything you type is echo'd back to the user's terminal by the kernel's tty subsystem. There's other special handling, too, for erase characters, Ctrl+R, Ctrl+Z, and so on.
Certain programs, (editors in particular) that run from a command line don't need or want this. For this reason they signal the kernel with an IOCTL call against the tty (terminal) device that they don't want this behaviour. They don't want special characters to do special things, either. Instead they ask the kernel for a "raw" mode. In particular, editor's like vim turn off various "echo settings". All this applies to real tty terminals on a computer's serial lines, or the virtual terminals at Alt+Ctrl+F4, or the really virtual terminals you get when you run something like gnome-terminal under a GUI.
Such programs are supposed to reset any modes they change on the virtual tty they are using before they quit, either by entering a quit editor command or by taking a signal (from Ctrl+C) for example.
If they fail to do this properly the tty is left in the funny state you have discovered. Since programs can fail to reset the terminal, the reset
command was written to allow the user to recover.
I assume the interrupt is messing with the python software you are running. I'd guess that that program isn't getting a chance to reset the terminal, or is simply failing to do so.
In the vim case, when I run your example I get the same behaviour you describe. I also see a message "Vim: Warning: Input is not from a terminal" (It goes away when you reset). This is because vim isn't started normally from the shell. Instead the 'grep
' and 'xargs
' commands have been using the standard input, normally occupied by the tty, for purposes of passing the file names from grep
tto xargs
.
In your posted output from stty -a
we can see "-echo", also confirming that this is the problem. If you were to kill vim in such a way that it couldn't handle the signal gracefully you would probably see the same problem.
The problem is described elsewhere at https://stackoverflow.com/questions/3852616/xargs-with-command-that-open-editor-leaves-shell-in-weird-state.
A solution for the vim case is avoid xargs and use instead:
vim $(grep foo * -l)
Here the list of files is constructed by the shell, as it had been by xargs, but the shell is calling vim, which is directly connected to the tty. There is warning message sent to the error output file, and vim sets and resets the tty settings correctly.
An alternative to reset
that doesn't clear the screen is stty sane
.
More references here, and another interesting one here. Another interesting solution is given in an answer to https://stackoverflow.com/questions/8228831/why-does-locate-filename-xargs-vim-cause-strange-terminal-behaviour.
Command cd
can not be an executable
In a shell, cd
is used to "go into another directory", or more formally, to change the current working directory (CWD). It's impossible to implement that as an external command:
The directory belongs to a process
The current working directory is the directory that is used to interpret relative paths to obtain a full path that can be used to access files.
Relative paths are used in many places, and the interpretation in one process should not influence another process.
For this reason, every process has its own current working directory.
cd
is about changing the current working directory of the shell process, for example, bash
.
If it were an external command, an executable in the path, running that executable would create a process with its own working directory, without influencing that of the current shell. Even if the external command would change its directory, that change goes away when the external process exits.
Shell builtin commands
So it makes no sense to run an external command for the task of cd
. The command cd
needs to apply a change to the currently running shell process.
To do that, it is a “builtin command” of the shell.
Builtin commands are commands that behave similar to external commands, but are implemented in the shell (so cd
is not part of the coreutils). This allows the command to change the state of the shell itself, in this case, to call chdir()
see (see man 2 chdir
);
About which
Now, the answer to the title question is easy:
The executable command which
can not tell us that cd is a builtin command because an executable command does not know anything about builtins.
Alternative type -a
As an alternative to which
, you can use type -a
; It can see executable commands and builtins; Additionally, it sees aliases and functions - also implemented in the shell:
$ type -a cd
cd is a shell builtin
$ type -a type
type is a shell builtin
$ type -a which
which is /usr/bin/which
which is /bin/which
Best Answer
You likely have a long PATH set and, to find an executable, the shell needs to search the path. To avoid that time consuming process every time that you want to run a program, the shell may keep a list of programs that it has already found. That list is called a "hash." When the shell says that
which
is hashed, it means that it has already done the PATH search and foundwhich
and saved its location in the hash.man bash
explains it as follows:While the hash normally speeds up shell operations, there is one case where it causes problems. If you update your system and, as a result, some executable moves to a new location, the shell may get confused. The solution is to run
hash -r
which causes the shell to forget all the hashed locations and search the PATH from scratch.Why are some executables missing from the hash?
An executable is not placed in the hash until after you execute at least once. Observe:
python
is hashed only after it has been executed.How to examine what is in bash's hash
The contents of the hash are available in the
bash
arrayBASH_CMDS
. You can see what is in it with the commanddeclare -p BASH_CMDS
. When a new shell or subshell is opened, the hash is empty. Commands are added one by one as they are used. From a newly opened shell, observe: