ls lista o conteúdo dos diretórios se passado como argumentos. Quando você passa * para seu shell, seu shell expande * para todos os arquivos e diretórios (no diretório de trabalho atual) e então ls pega esses argumentos e os processa. Como resultado, ele lista o conteúdo desses diretórios especificados junto com os arquivos no diretório de trabalho atual.
No caso de echo , o shell expandiu o * e você não obtém o conteúdo do diretório, porque o echo apenas imprime o que foi passado para ele como argumentos.
Você pode dizer a ls para não mostrar o conteúdo dos diretórios usando ls -d * .
Dada esta configuração:
$ mkdir dir{1,2}
$ touch file{1..3} dir{1,2}/morefiles{1..5}
$ ls
dir1 dir2 file1 file2 file3
Passar * para o shell (bash) faz com que o bash expanda * para dir1 dir2 file1 file2 file3 . Usar echo com * fará com que echo exiba o que o shell expandiu de * :
$ echo *
dir1 dir2 file1 file2 file3
No entanto, os argumentos dos diretórios ls irão, por padrão, expandir o conteúdo dos diretórios listados:
$ ls *
file1 file2 file3
dir1:
morefiles1 morefiles2 morefiles3 morefiles4 morefiles5
dir2:
morefiles1 morefiles2 morefiles3 morefiles4 morefiles5
$
Você pode descartar o * on ls (conforme ilustrado depois de criar a estrutura de diretórios de exemplo) ou, conforme mencionado em minha resposta, usar ls -d * :
$ ls -d *
dir1 dir2 file1 file2 file3
Isso impede que ls mostre o conteúdo de dir1/ e dir2/ .