Assuming that you are using GNU tools, you could use GNU basename
to get the names of all subdirectories in a particular directory. You could then use paste
to format this as a space-delimited list.
basename -a /some/path/*/ | paste -d ' ' -s -
The above command uses the fact that GNU basename
has an -a
option to return the filename portion of multiple pathnames given as operands on its command line.
We use a file-globbing pattern ending in /
to generate the pathnames for GNU basename
. Only directories can match such a pattern.
In the end, the paste
creates the space-separated list from the newline-separated list produced by GNU basename
.
Note that it would be difficult to parse the generated list of filenames if any of the original names of directories contain space characters.
Note that if the directory contains symbolic links, this method will try to follow those symbolic links.
Restricting us from using any external tools, we could use an array in the bash
shell to store and manipulate the directory paths.
shopt -s nullglob
topdir=/some/path
dirpaths=( "$topdir"/*/ )
dirpaths=( "${dirpaths[@]#$topdir/}" )
dirpaths=( "${dirpaths[@]%/}" )
printf '%s\n' "${dirpaths[*]}"
The above shell code expands the same globbing pattern as we used in the first part of this answer but stores the resulting directory paths in the array dirpaths
. It then deletes the known prefix $topdir/
from each element of the array and the trailing /
before printing the array as a single string of space-delimited names. The delimiter used between the names on the last line will be the first character from $IFS
, which by default is a space.
Using find
, you could look for subdirectories in the particular top directory you're interested in while making sure not to return the top directory itself. You would also stop find
from progressing into the subdirectories.
topdir=/some/path
find "${topdir%/}/." ! -name . -prune -type d -exec basename {} \; | paste -d ' ' -s -
The above command avoids the search starting point using a negated -name
test, and it prunes the search tree with -prune
so that find
does not recurse down into any subdirectories. We call basename
for each found directory which outputs the filename of the directories onto separate lines. As the last step, we're piping the result from find
through paste
to format the output into a space-separated list on a single line.
With GNU find
, you could write this as
find /some/path -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | paste -d ' ' -s -
Using find
like this will list directories with hidden names, and you will not see any symbolically linked directories.
In the zsh
shell, you would be able to use a more advanced shell globbing pattern to pick out the filenames of only directories and print them in one go.
print -r -- /some/path/*(/:t)
This command uses a glob qualifier, /:t
, consisting of two parts, affecting the preceding globbing pattern /some/path/*
. The /
makes the pattern only match directories (not symbolically linked ones; for that use -/
), while :t
extracts the "tail" of each generated pathname, i.e., the filename component.
The print -r
command prints its arguments with spaces as delimiters while avoiding expanding escape sequences like \n
or \t
in the data. Using --
to delimit the operands from the options (also works with -
like in the ksh
shell) makes sure directory names resulting from the glob expansion are not taken as options even if they start with -
.
You could use this from within the bash
shell to generate your list.
zsh -c 'print -r -- /some/path/*(/:t)'
ls
(as your question title indicates). You seem to be presupposing that the solution needs to involvels
, but you don't explain why this can't or shouldn't be done via other means likefind
. – NotTheDr01ds Sep 27 '21 at 19:30