12

The ls can give a result like

[root@localhost ~]# cd /etc/yum.repos.d/
[root@localhost yum.repos.d]# ls

CentOS-Base.repo CentOS-Debuginfo.repo CentOS-Media.repo CentOS-Vault.repo

But actually I hope to just find out CentOS-Base.repoCentOS-Debuginfo.repo and CentOS-Vault.repo but not CentOS-Media.repo. So I run this command

ls [^\(Media\)]

But I get a error information.How should I do?

10

In most simple case you may use the following (in case if the 1st subword is static CentOS):

ls CentOS-[BDV]*

  • [BDV] - character class to ensure the second subword starting with one of the specified characters

or the same with negation:

ls CentOS-[^M]*

If you want to ignore all filenames that contain the M character, with the GNU implementation of ls (as typically found on CentOS), use the -I (--ignore) option:

ls -I '*M*'

-I, --ignore=PATTERN
do not list implied entries matching shell PATTERN

To ignore entries with Media word:

ls -I '*Media*'

Those patterns need to be passed verbatim to ls, so must be quoted (otherwise, the shell would treat them as globs to expand).

  • Why we cannot use ls *[^M]* – yode Jun 23 '17 at 12:00
  • The ls CentOS-[^M]* work,but the ls *[^M]* don't.. – yode Jun 23 '17 at 12:07
  • @yode, depends on what exactly you want to ignore: M character occurence OR Media word? – RomanPerekhrest Jun 23 '17 at 12:10
  • But in my case,only that file have a charater M. – yode Jun 23 '17 at 12:11
  • 3
    The patterns are greedy: the first * matches everything except the last letter, the [^M] matches the last letter since none of them ends with M and the trailing * matches the empty string. So they all match. And even if something ended with M, it would still match, assuming there was something different from M somewhere: e.g. if you had a file called OOM, the first star would match the first O, the [^M] would match the second O and the trailing start would match the M. – NickD Jun 23 '17 at 12:49
12

A few shells have negation globbing operators:

  • zsh -o extendedglob:

    ls -d -- ^*Media*
    ls -d -- *.repo~*Media* # ~ is "except" operator
    
  • ksh, zsh -o kshglob, bash -O extglob:

    ls -d -- !(*Media*)
    
  • bash:

    GLOBIGNORE='*Media*'
    ls -d -- *
    
  • ksh:

    FIGNORE='@(*Media|.)*'
    ls -d -- *
    
6

One option is to use find with the -not -name flags. I.e. find . -not -name CentOS-Media.repo. If you don't want to recurse down the directory structure, add -maxdepth 1 flag.

Alternatively, one may write the following (which is much more complex, but I forgot about -not flag and posted this answer originally, so I will not delete this part):

find . -print0 | grep --invert-match -z "CentOS-Media.repo$" | tr '\0' '\n'

You need to force find to separate filenames with null byte, so that newlines in filenames won't break anything down. Hopefully, grep supports this kind of separator with flag -z. You may want to revert to the typical separation (i.e. null byte -> new line) with tr '\0' '\n'

  • 1
    You can also get null-separated filenames with printf '%s\0' * which doesn't recurse and doesn't include dotfiles by default (but some shells have an option for that). You also don't need -E for that regexp, but to be picky you should either backslash the . or put it in [], and you do depend on GNU find and grep. – dave_thompson_085 Jun 23 '17 at 18:10
4

The easiest way is to use find. Do:

find . -maxdepth 1 -type f ! -name "CentOS-Media.repo"

Here "f" means search for regular files only (excludes symlinks to regular files though; with GNU find, use -xtype f instead to include them). If you want to search for directories, pass "d" instead.

(-maxdepth while initially a GNU extension is now quite common. If your find doesn't support it, you can replace -maxdepth 1 with the standard ! -name . -prune).

see the find man page for more awesome features.

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.