As you know, ~
expands to your home directory. But what you seem to have missed is that ~john
expands to the home directory of the user named "john".
Check your /etc/passwd
file:
% grep ker /etc/passwd
kernoops:x:107:65534:Kernel Oops Tracking Daemon,,,:/:/bin/false
It has a "system user" named kernoops
(for internal bug reporting reasons). So when you type cd ~k
Tab the shell gives preference(1) to user name expansion before local directories expansion, and you have cd ~kernoops
. Now it results that user "kernoops" home dir is /
, so it cd
s to it.
In my shell, zsh
, I have no double slash(2):
[:/] % cd ~kernoops/
[:/] % pwd
/
[:/] % cd //
[:/] % pwd
/
As an aside, this directory name is not well thought. It will need triple care in scripts and whatever. The only worst idea I can think is embedding a tab in it...
Footnotes:
(1) In zsh
, even if I have a directory named ~xdir
and no user starting with x
; doing cd ~x
Tab does not expand and not quoting the ~
gives error:
[:~/tmp/x] % mkdir \~xdir
[:~/tmp/x] % ls
~xdir
[:~/tmp/x] % cd ~xdir
zsh: no such user or named directory: xdir
[:~/tmp/x] 1 %
(2) I seem to remember that posix made an exception for the initial //
in a path --- it should be maintained because some old unix variant (I used the apollos with Domain/OS that had that) used //machine-name/...
to seamless access other machines' filesystem in the local network (security was not invented yet). So probably bash is right here. If you do cd ///
you will have the normal /
in both shell, though.
Yes, found on unix.se!
The exact same command should work fine in a script:
#!/usr/bin/env bash
find te*/my\ files/ -print
If you need to have it as a variable, it gets a bit more complex:
#!/usr/bin/env bash
search='te*/my\ files/'
eval find "$search" -print
WARNING:
Using eval
like that is not safe and can result in executing arbitrary and possibly harmful code if your file names can contain certain characters. See bash FAQ 48 for details.
It's better to pass the path as an argument:
#!/usr/bin/env bash
find "$@" -name "file*"
Another approach is to avoid find
altogether and use bash's extended globbing features and globs:
#!/usr/bin/env bash
shopt -s globstar
for file in te*/my\ files/**; do echo "$file"; done
The globstar
bash option lets you use **
to match recursively:
globstar
If set, the pattern ** used in a pathname expansion con‐
text will match all files and zero or more directories
and subdirectories. If the pattern is followed by a /,
only directories and subdirectories match.
To make it act 100% like find and include dotfiles (hidden files), use
#!/usr/bin/env bash
shopt -s globstar
shopt -s dotglob
for file in te*/my\ files/**; do echo "$file"; done
You can even echo
them directly without the loop:
echo te*/my\ files/**
Best Answer
Yes, the shell understands
*
as all files with any characters in the directory and*.rb
as all files with any characters and ending.rb
, and expands it as such.The
find
command itself accepts globbing.If you don't quote the
*
then the shell will expand it before thefind
command sees its argument, so instead of a glob*.rb
passed tofind
, the names of all files matching the glob in the directory will be passed tofind
, andfind
will try to interpret them as arguments, which will likely result in an error, or at least not what you want (it will work correctly only if there are no matching files in the current directory)