0

Where is the difference? Do these distributions handle something differently? Or does the configuration vary? What do I have to compare and adjust? I would prefer find * not to match the wildcard itself.

  • The results differ because one host matches * too: In contrast to RedHatHost, DebianHost searches for * as well. But there is no file named *.
  • Platforms: Debian Wheezy 7, Red Hat Enterprise Linux 7. Bash is always used.
  • grep is supposed to look for the host's name in every file in /etc. There could only be one match for each host: /etc/hostname on DebianHost and cat /etc/sysconfig/network on RedHatHost.
DebianHost# cat $(find /etc/ *) 2>/dev/null | grep $(hostname)
find: `*': No such file or directory
DebianHost
DebianHost# 
DebianHost# 
DebianHost# 
DebianHost# hostname
DebianHost
DebianHost# 
DebianHost# 
DebianHost# 
DebianHost# pwd
/root
DebianHost# 
DebianHost# 
DebianHost# 
DebianHost# ls -1 .
DebianHost# 
DebianHost# 
DebianHost# 
DebianHost# grep -r $(hostname) /etc
/etc/hostname:DebianHost
DebianHost# 
DebianHost# 
DebianHost# 
DebianHost# find /etc/ * | grep -e "*"
find: `*': No such file or directory
DebianHost# 

RedHatHost# cat $(find /etc/ *) 2>/dev/null | grep $(hostname)
HOSTNAME=RedHatHost
RedHatHost# 
RedHatHost# 
RedHatHost# 
RedHatHost# hostname
RedHatHost
RedHatHost# 
RedHatHost#
RedHatHost#  
RedHatHost# pwd
/root
RedHatHost# 
RedHatHost# 
RedHatHost# 
RedHatHost# ls -1 .
anaconda-ks.cfg
install.log
install.log.syslog
RedHatHost#
RedHatHost# 
RedHatHost# 
RedHatHost# grep -r $(hostname) /etc/
/etc/sysconfig/network:HOSTNAME=RedHatHost
RedHatHost# 
RedHatHost#
RedHatHost# 
RedHatHost# 
RedHatHost# find /etc/ * | grep -e "*"
RedHatHost# 
3

* containing a non-quoted wildcard character (being itself a wildcard character), it is considered as a glob and expanded by the shell to the list of files that match that pattern.

That specific pattern (*), matches any non-hidden file names, so, before calling find the shell will expand it to the list of non-hidden files in the current directory, so if the current directory contains a file called foo and another called foo bar, it will call find with those arguments:

"find", "/etc/", "foo", "foo bar"

If there's no non-hidden file in the current directory, the behavior varies among shells. csh, tcsh, fish, zsh will issue a no match error message and not run the command, while the POSIX shells will still call find but with the pattern unexpanded. So in those, find will be called with these arguments:

"find", "/etc/", "*"

(which is asking find to find all the files in /etc/ and in * (which in this case doesn't exist)). Most probably, on RedHat, you're calling that command from a directory that does contain non-hidden files, while on Debian, you're calling it from on that contains only hidden files.

By the way

cat $(find /etc/ *) 2>/dev/null | grep $(hostname)

is wrong.

You should write it:

find /etc -type f -exec cat {} + | grep -Fe "$(hostname)"

Or probably more useful:

find /etc -type f -exec grep -Fe "$(hostname)" {} +

Or since both Debian and RedHat have the GNU grep:

grep -rFe "$(hostname)" /etc
4

The * in both cases is being expanded by the shell. You are probably coming from Windows, where the shell never expands wildcards, just passes them literally to the program. Unix shells don't work like that.

The best way to see this is to say echo * on both machines. On your Debian machine, it will print * because you are running it in a directory with no files in it, so the * does get passed through to the program. (I'm assuming you're running Bash, which passes the wildcard through to the called program when it doesn't match anything.) On your Red Hat box, it will say

anaconda-ks.cfg install.log install.log.syslog

That is, the three file names in the directory you're running it in get passed to echo which prints them out with a single space separating its arguments.

I'm not sure what effect you're trying for with find /etc/ *, because that's not really a sensible find command. If you just want to list all files in the /etc tree, find /etc will suffice.

  • 2
    It might be worth mentioning that the passing of the unexpanded * in the no-match case can be switched off in some shells e.g. bash's shopt -s nullglob – steeldriver Jun 9 '14 at 11:39
  • @steeldriver, nullglob would cause more harm than help (if applied globally in ~/.bashrc), that's really one that should only be applied on a case by case basis (like with the (N) globbing qualifier in zsh). failglob (to work like zsh) would be more useful though would still break bogus scripts sourced by ~/.bashrc like some bash-completion ones. – Stéphane Chazelas Jun 9 '14 at 11:43
  • Ultimately, whether nullglob or failglob, I think you're fighting against POSIX here, so if you want that behavior, you should probably just switch to zsh or another shell that behaves that way by default. Leave Bash alone, for shell script compatibility. – Warren Young Jun 9 '14 at 11:48
2

find /etc * does not look for a file named *. The way it behaves depends on whether the current directory is empty¹.

  • If it is empty, then the shell expands find /etc * to itself; find sees that it is told to traverse the two directories /etc and *, and complains that * doesn't exist (in addition to listing all the files under /etc). This is what you're seeing on Debian.
  • If it is not empty, then the shell expands find /etc * to find /etc followed by the list of files in the current directory. So find lists all the files under /etc, and also all the files under the current directory except for the current directory itself. This is what you're seeing on RHEL.

The difference has nothing to do with the distribution, you're conducting the test in different contexts on the two machines.

None of this has anything to do with looking for a file named *. If you want to look for a file by name, you need to use the -name predicate: find /etc -name somename. The argument to -name is a wildcard pattern like those of the shell. Since the character * is a wildcard, in order to look for a file named *, you need to protect the wildcard with a backslash: find -name '\*'. Note the two levels of quoting: the single quotes to prevent expansion of the characters \ and * by the shell (you could use find -name "\\*" or find -name \\\* instead), so that find sees the pattern \*, and the backslash so that the pattern is “literal *” rather than “any sequence of characters”.

* is a valid, but unusual, character in file names. If you didn't actually want to match a character whose name is *, but any file, then you can write find /etc -name '*' (again, you need to quote the * so that it is not expanded by the shell), but this is exactly equivalent to just find /etc.

¹ More precisely, whether the current directory contains files other than dot files, i.e. whether it contains files whose name doesn't begin with ..

  • See also find /etc ! -name '.*' if the point is to exclude dot-files like the shell's * does. – Stéphane Chazelas Jun 9 '14 at 11:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.