0

There are already many answers for this similar question. But none of them satisfy my requirements. I want

  1. List all directories under a directory without using glob (*) syntax, i.e. I want to directly use lsdir somedir

  2. Output should containing basename of the directories like when you just use ls, like:

    $ lsdir path/to/some/dir
    dir1 dir2 dir3 dir4
    

    but not this:

    $ lsdir path/to/dir
    path/to/dir/dir1 path/to/dir/dir2 path/to/dir/dir3 path/to/dir/dir4
    

    To satisfy requirement 1, it seems feasible to define a function, but anyway we are going to use -d option, to list the directories themselves of the ls command parameters.

    And when using -d option, ls list directory names with its parent prepended, like above.

  3. ls format (color, align, sort) should be preserved.

    To satisfy requirement 2, we can use find but in this way we lose all the ls output format, like coloring (based on customized dircolors theme), alignment (output in aligned columns), sorting (sorting customized with various flags and in a column-first manner), and maybe some other things.

I know it's too greedy to want this many features simultaneously, and indeed I can live without all of them.

It's possible to emulate ls output format manually but that's too inconsistent.

I wonder if there is a way to achieve this and still utilize ls, i.e. how to achieve requirement 2 using ls.

Masquue
  • 166
  • 8

2 Answers2

1

This may be what you're looking for:

cd path/to/dir && dirs=(*/) && ls -d "${dirs[@]%?}"

or, perhaps

(shopt -s nullglob; cd path/to/dir && dirs=(*/) && ((${#dirs[@]} > 0)) && ls -d "${dirs[@]%?}")

The second version runs in a subshell and prints nothing if there is no any subdirectory inside path/to/dir.

M. Nejat Aydin
  • 9,597
  • 1
  • 7
  • 17
  • OMG, thank you so much! I didn't expect it could be a simple `cd` command. Maybe just need to wrap the cd in a subshell to not affect cwd of current shell and do some processing of possible additional options. – Masquue Jan 25 '22 at 12:20
  • @Masquue yes, you can run it in a subshell by enclosing the whole command by `(` and `)`. Also I've added a second version in case there is no subdirectory inside the main directory. – M. Nejat Aydin Jan 25 '22 at 12:25
  • Love your second version. And may I ask if I want to improve a little more based on your answer, should I edit your answer or post another answer myself? – Masquue Jan 25 '22 at 12:32
  • And I wonder why I can't at you in comment, forgive me I'm new, is this because of the space in your username? What's the correct format to use? – Masquue Jan 25 '22 at 12:35
  • @Masquue The author of the answer is notified whenever a comment is written under the answer, so I think there is no need to at the author, perhaps, unless there are other commentators. I don't know the answer of your second question. You can ask or search for it in the [meta](https://meta.stackoverflow.com/). – M. Nejat Aydin Jan 25 '22 at 12:48
  • Which one do you mean by my second question? Is it the one about my edit or answer question? – Masquue Jan 25 '22 at 12:53
  • @Masquue Yes, it is. – M. Nejat Aydin Jan 25 '22 at 12:54
0

Based on @M. Nejat Aydin's excellent answer, I am going to improve a little more to make it a useful command, especially with respect to processing options and multiple directories.

list_directories() {
  local opts=()
  local args=()
  for i in $(seq $#); do
    if [[ "${!i}" == -* ]]; then
      opts+=("${!i}")
    else
      args+=("${!i}")
    fi
  done
  (( ${#args[@]} == 0 )) && args=('.')
  local -i args_n_1=${#args[@]}-1
  for i in $(seq 0 $args_n_1); do
    if (( ${#args[@]} > 1 )); then
      (( i > 0 )) && echo
      echo "${args[i]}:"
    fi
    (
      shopt -s nullglob 
      cd "${args[i]}" && 
      dirs=(*/) && 
      (( ${#dirs[@]} > 0 )) && 
      ls -d "${opts[@]}" "${dirs[@]%?}" 
    )
  done
}
alias lsd=list_directories

This lsd can be used with any number of ls options and directories freely mixed.

$ lsd -h dir1 dir2 -rt ~

Note: Semantic meaning changes when you use globs with lsd.

lsd path/to/dir* list all directories under each directory starting with "path/to/dir".
To list all directories starting with "path/to/dir", use plain old ls -d path/to/dir*.

Masquue
  • 166
  • 8