In the historical Unix system, directories were implemented as normal files with a special mode indicating that they were directories. This is no longer true on many modern filesystems and operating systems. When it comes to the on-disk structures, a directory may or may not be represented as a file-like blob of storage, depending on the filesystem. When it comes to the operating system interfaces, there are separate system calls to access directories and regular files: opendir
, readdir
, rewinddir
, closedir
corresponding to open
, read
, rewind
, close
respectively.
Software that reads a directory must use the directory-specific interfaces. When you open a directory in Vim, it doesn't load its contents, the way it would for a file. Vim doesn't have any native handling for directories: if you run vim --noplugin
on a directory, Vim complains that "foo" is a directory
and won't save the buffer for that same reason.
The primary goal of netrw is to access remote files (over a variety of network protocols). Since you can't easily go and run commands like ls
and cp
in the directories containing these remote files, netrw includes code to browse directories and manipulate them — it's a file manager as well as a remote file browser. These file manager capacities make sense locally as well as remotely, so netrw registers itself as a handler for local directories in addition to remote files and directories.
This isn’t really related to quoting, but rather to argument processing.
Consider the risky example:
find -exec sh -c "something {}" \;
This is parsed by the shell, and split into six words: find
, -exec
, sh
, -c
, something {}
(no quotes any more), ;
. There’s nothing to expand. The shell runs find
with those six words as arguments.
When find
finds something to process, say foo; rm -rf $HOME
, it replaces {}
with foo; rm -rf $HOME
, and runs sh
with the arguments sh
, -c
, and something foo; rm -rf $HOME
.
sh
now sees -c
, and as a result parses something foo; rm -rf $HOME
(the first non-option argument) and executes the result.
Now consider the safer variant:
find -exec sh -c 'something "$@"' sh {} \;
The shell runs find
with the arguments find
, -exec
, sh
, -c
, something "$@"
, sh
, {}
, ;
.
Now when find
finds foo; rm -rf $HOME
, it replaces {}
again, and runs sh
with the arguments sh
, -c
, something "$@"
, sh
, foo; rm -rf $HOME
.
sh
sees -c
, and parses something "$@"
as the command to run, and sh
and foo; rm -rf $HOME
as the positional parameters (starting from $0
), expands "$@"
to foo; rm -rf $HOME
as a single value, and runs something
with the single argument foo; rm -rf $HOME
.
You can see this by using printf
. Create a new directory, enter it, and run
touch "hello; echo pwned"
Running the first variant as follows
find -exec sh -c "printf \"Argument: %s\n\" {}" \;
produces
Argument: .
Argument: ./hello
pwned
whereas the second variant, run as
find -exec sh -c 'printf "Argument: %s\n" "$@"' sh {} \;
produces
Argument: .
Argument: ./hello; echo pwned
Best Answer
~
is a special name expanded by the shell,.
and..
are real proper directory names, so no expansion is done by the shell there.