I assume you are aware, generally, of the notion of parent and child processes. Particularly with respect to shells. Let's say I'm in the shell. My prompt shows the current directory ($PWD
)...
[/bar] $ sh # launch child shell
[/bar] $ cd /foo
[/foo] $ exit
[/bar] $ # back in the parent it's still /bar
Similarly...
[/bar] $ sh # launch child shell
[/bar] $ myvar=something
[/bar] $ exit
[/bar] $ echo $myvar
[/bar] $ # parent knows nothing about myvar
Parent processes are, broadly speaking, not altered by the things that occur in their children. It is no different when Vim is the parent of a shell process.
Generally, if the parent wants to see what happens in the child the parent needs to look for it. In the case of Vim the primary way to look in at the child/shell is to capture its commands' output using the system()
function or to use :r !cmds
to read the output into a buffer.
As for the other version of the question that OP indicated interest in, "Why does :cd change the directory?" I'll assume "the directory" here refers to the current working directory of a subsequently launched shell ...if we were talking about Vim's working directory a fair answer to that would be "Because that's what the command does."
:cd
is a Vim command operating in the Vim program space and the directory it changes is associated with the Vim process. One way this is useful is if you try to edit a file without a path (e.g. foo.txt
) Vim will look in this directory for the file. A subsequently launched shell process inherits Vim's working directory because, well, that's how they've implemented it. They didn't have to do it that way. Vim is not a "shell process" it's a Vim process. But it's a natural way to do things1 and many are accustomed to partial inheritance with nested shell processes...
[/bar] $ myvar=this # ordinary variable
[/bar] $ MYVAR=that; export MYVAR # environment variable
[/bar] $ cd /foo # change current working dir
[/foo] $ sh # launch child shell
[/foo] $ echo $MYVAR # c.w.d. inherited
that # env vars inherited
[/foo] $ echo $myvar
# regular vars NOT inherited
[/foo] $
Running a shell command from Vim mimics such behavior.
1 In fact, most common operating systems support this behavior automatically and by default. That is when you exec or fork or whatever to launch a new process there is inheritance of some defined subset of the parent process' context/environment
!
= "not";cd
= "change directory".!cd
= "don't change the directory".!
is vim's "run in a shell" command. It has, in this context, absolutely nothing to do with a logical NOT. By your reasoning:!ls
would be "don't list files", but that command absolutely will list files.: !
in vi runs the rest of the string as a command in a subshell. That's why the directory of the parent (vi) is unchanged. (I love the idea of a command that says "whatever I say, don't do it".) However, vi has an actual command cd that it actions by changing its own directory. But when you exit, it still cannot have changed the cwd of the shell you started vi from.