0

I just learned that ls cannot handle wildcards (ls '*' does not work) and therefore the shell does it before ls is invoked. So the behaviour of ls may be considered as logically for people working 20 years with unix, for me it is not and I not keep with ls.

I want a directory command that scans for files and directories and afterwards applies the wildcard filter. So that a ls cr* does not list files inside the directory "cron.daily".

I know that there is ls -d and I made an alias lx=ls -lartd. But then I recognized that ls -d without any argument for some secret reason does not work at all.

Is there any replacement which I can install?

6
  • you can write your own script to encapsulate the ls command and behave the way you want it to, as a solution.
    – MelBurslan
    Jun 16, 2016 at 22:57
  • 1
    It's not a secret reason. It has been on SuperUser at superuser.com/a/344896/38062 for four and a half years, and that wasn't exactly revelatory.
    – JdeBP
    Jun 17, 2016 at 8:24
  • 2
    Even if you manage to achieve this, you'll then have a command which handles wildcards completely differently from every other program. That makes this an incredibly bad idea - ou'll be confusing yourself constantly because you'll be using two similar-seeming but different wildcard implementations. You would be much better off just putting in the small effort required to a) use shell globbing effectively and b) learn and understand the various options to programs like ls. Unix is not MS-DOS or CMD.EXE....it's also not hard to learn basic things like this.
    – cas
    Jun 17, 2016 at 12:03
  • sorry, but the machine has to do what I want and not vice versa. if it does not, it will be replaced.
    – user175477
    Jun 17, 2016 at 12:12
  • 1
    ls -d works! It just displays the current directory (.), like you asked it to.
    – Kusalananda
    Jun 17, 2016 at 12:26

3 Answers 3

5

You could use find, which (on many platforms) has an -ls option. So you could do

find . -maxdepth 1  -name '*' -ls

But if it is hard to remember ls -d, you may not find find an improvement.

1
  • find -maxdepth 1 -ls -name '*' is near to what I want.
    – user175477
    Jun 17, 2016 at 12:10
1

If what you want is a command cmd so that cmd cr* lists cron.daily itself, then that command is ls -d. If that's too much typing, you can define an alias. For example, put this line in your ~/.bashrc:

alias l='ls -d'

Then running l cr* will display information about cron.daily itself, while ls cr* will list the contents of the directory cron.daily (and of any other directory matching the pattern and any other non-directory matching the pattern).


ls -d does work. It does exactly what you asked it to: it lists the current directory itself. That may not be very useful — ls -ld is somewhat useful, but not ls -d — but computers do what you tell them to do, they don't read your mind to find out what you wanted.

If what you want is to get the effect of ls -d, except that with no arguments you get a listing of the current directory, you can define a function.

l () {
  if [ $# -eq 0 ]; then
    ls -l
  else
    ls -ld "$@"
  fi
}

Then l cr* lists the files whose name begins with cr, even if they're directories. And l cron.daily lists cron.daily itself, not its contents; you can use ls cron.daily or l cron.daily/* to list the contents. But l lists the files in the current directory.

With the function above, l -a will pass the -d flag. If you don't want that, you can make the function discard options when determining whether it has file name arguments.

l () {
  local a
  a=("$@")
  while [[ $1 = -* ]]; do shift; done
  if [ $# -eq 0 ]; then
    ls -l "${a[@]}"
  else
    ls -ld "${a[@]}"
  fi
}

If what you want is a command cmd so that cmd cron.daily lists the files in the subdirectory cron.daily, but cmd cd* lists cron.daily itself, then this is impossible in bash. By the time the command is executed, the shell has expanded the wildcards, and the command cannot know whether you used wildcards or not.

This is possible in zsh, because in zsh you can disable wildcard expansion for a specific command. Zsh isn't the default shell on most systems, but it's just one package installation away. In zsh, you can define an alias with the noglob prefix:

alias l='noglob list_files_or_directory_contents'

You'll then need to define that list_files_or_directory_content function — your requirement is uncommon, so it probably doesn't exist yet. Here's a function that I think does what you're after: list the contents of directories specified directly, and list the directories themselves if they come from a wildcard expansion.

list_files_or_directory_contents () {
  local arg matches
  setopt local_options no_csh_null_glob no_null_glob no_nomatch no_glob_subst no_sh_word_split
  if (($#==0)); then
    # No arguments: list the directory itself
    ls -l
  else
    for arg; do
      matches=($~arg)
      if [[ $#matches -eq 1 && $matches[1] == $arg ]]; then
        # Not a wildcard pattern: list the directory contents if it's a directory
        ls -l -- $arg
      else
        # Wildcard pattern: list the matches themselves, even the ones that are directories
        ls -ld -- $matches
      fi
    done
  fi
}
2
  • "ls -d" would do what I want. But "ls -d" without any argument does nothing, so it is useless. "ls" without arguments is asuming "." as the argument. If I could change that so "ls" without argumens would be "ls *" then it would be fine. Or is there a difference between "ls ." and "ls *"?
    – user175477
    Jun 17, 2016 at 11:48
  • @jms ls . causes ls to list the contents of the current directory (unless the -d option is passed). With ls *, the shell lists the contents of the current directory and passes the resulting list to ls. I edited my answer, is what I added what you meant? Jun 17, 2016 at 18:19
0

I think this will do what you want. The approach could, I suppose, be applied to as many utilities as you wanted. But I really wouldn't recommend that because it will break your "finger habits" when you move to a new system that you haven't yet tweaked.

ls() { [[ 0 == $# ]] && set -- *; /bin/ls -d "$@"; }

By extension your alias would then be replaced with this function:

lx() { [[ 0 == $# ]] && set -- *; /bin/ls -lartd "$@"; }

You must log in to answer this question.