I am trying to understand how the ls
command works, and I presume there is a shell script which defines ls
somewhere in the filesystem. This correct? If so, where can I find it?

- 569
- 1
- 5
- 8
3 Answers
ls
uses opendir()
and readdir()
to step through all the files in the directory. If it needs more information about one of them it calls stat()
. Read the source of course, but a very handy shortcut is :
# strace ls
The key part with a couple comments is :
Get the directory entries
open(".", O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_DIRECTORY|O_CLOEXEC) = 3
fcntl64(3, F_GETFD) = 0x1 (flags FD_CLOEXEC)
getdents64(3, /* 53 entries */, 32768) = 1744
getdents64(3, /* 0 entries */, 32768) = 0
close(3) = 0
Verify stdout is a character device
fstat64(1, {st_mode=S_IFCHR|0600, st_rdev=makedev(136, 0), ...}) = 0
map stdin into memory. (Not sure why, see the source)
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1,
0) = 0xb73ff000
write the directory entries to stdout and wrap-up
write(1, "bin Desktop Documents Downloa"..., 91bin Desktop
Documents Download Music Pictures Public public_html Templates
Videos
) = 91
close(1) = 0
munmap(0xb73ff000, 4096) = 0
close(2) = 0
exit_group(0) = ?

- 13,589
ls
is not a shell script, if you issue file
command, you will know it's an ELF 64-bit LSB executable file:
$ type -a ls
ls is aliased to `ls --color=auto'
ls is /usr/bin/ls #<---- now we know the file path of `ls`
ls is /bin/ls
$
$ file /usr/bin/ls
/usr/bin/ls: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=ddf8cdb3f1fd2e8263637b7c8ccea84fbf41ee3c, stripped
$
You can found the online source code here.
Or if your distro is RPM-based Linux distributions + dnf
, then you can:
$ rpm -qf /usr/bin/ls
coreutils-8.22-22.fc21.x86_64 #so now we know the package name is coreutils
$ sudo dnf whatprovides /usr/bin/ls #alternative way
Using metadata from Mon May 16 02:39:55 2016 (1 day, 23:03:50 hours old)
coreutils-8.22-22.fc21.x86_64 : A set of basic GNU tools commonly used in shell scripts
Repo : @System
coreutils-8.22-19.fc21.x86_64 : A set of basic GNU tools commonly used in shell scripts
Repo : fedora
coreutils-8.22-22.fc21.x86_64 : A set of basic GNU tools commonly used in shell scripts
Repo : updates
$
$ mkdir coreutils #optional
$ cd coreutils #optional
$ sudo dnf download --source coreutils
...
$ rpm2cpio coreutils-8.22-22.fc21.src.rpm |cpio -idmv
...
$ sudo rm coreutils-8.22-22.fc21.src.rpm #optional
$ unp coreutils-8.22.tar.xz
...
$ rm coreutils-8.22.tar.xz #optional
$ cd coreutils-8.22/
$ find . -iname 'ls*'
./lib/lseek.c
./lib/lstat.c
./src/ls.c #<---- now we know ls.c is here
./src/ls-vdir.c
./src/ls.h
./src/ls-ls.c
./src/ls-dir.c
./man/ls.x
./tests/ls
./tests/misc/ls-misc.pl
./tests/misc/ls-time.sh
./m4/ls-mntd-fs.m4
./m4/lstat.m4
./m4/lseek.m4
$ vi ./src/ls.c
Note:
coreutils-8.22-22.fc21.src.rpm is mine, your package number may vary.
Some commands like
type -a history
return "history is a shell builtin", you should look at the current shell source code, i.e.rpm -qf `readlink -f /proc/$$/exe`
(Detect current shell by command is tricky than you might think, this trick doesn't work infish
shell)In csh/tcsh shell, you should use
where history
because no suchtype
command. More details can be found here.You might also interest to try wildcard, e.g.
repoquery --resolve --archlist=src '*compress*'
to include not installed packages (Be careful if query command like '*uncompress*', in this case you need to remove the prefix 'un' to narrow down if first attempt '*uncompress*' failed). The output fromrepoquery
above need to remove middle 0: and optionally postfix with .rpm to get the correct full name which you can used to search in http://rpm.pbone.net , e.g. ncompress-0:4.2.4.4-3.fc21.src change it to ncompress-4.2.4.4-3.fc21.src.rpmYou can enable mirror debugging when dnf download source, in case mirror server down. See this.
[UPDATE]
In case you have rpm error due to invalid repos like me, this is how i fixed it:
$ sudo dnf config-manager --set-enabled '*' #Enable all repos, at anytime, check with `sudo dnf repolist all`
$ repoquery --resolve --archlist=src '*compress*'
Could not match packages: failure: repodata/repomd.xml from rpmfusion-free-rawhide-source: [Errno 256] No more mirrors to try.
http://free.nchc.org.tw/rpmfusion/free/fedora/development/rawhide/source/SRPMS/repodata/repomd.xml: [Errno 14] HTTP Error 404 - Not Found
...
$ repoquery --resolve --archlist=src --enablerepo='*source' --disablerepo='rpmfusion-free-rawhide-source' '*compress*' #not works too
...
$ sudo yum-config-manager --save --disablerepo=rpmfusion-nonfree-rawhide-source #for unknown reason, it doesn't work
$ sudo dnf config-manager --set-disabled rpmfusion-free-rawhide-source #for unknown reason, it doesn't work
$ grep -rnIH -D skip --color=always rpmfusion-free-rawhide-source /etc/yum.repos.d/
/etc/yum.repos.d/rpmfusion-free-rawhide.repo:17:[rpmfusion-free-rawhide-source]
$ sudo vi /etc/yum.repos.d/rpmfusion-free-rawhide.repo #Edit rpmfusion-free-rawhide-source from enabled=1 to enabled=0
$ repoquery --resolve --archlist=src '*compress*'#now should works :) repeat the `grep and vi` steps above if got error in other repos, in my case i have to disable rpmfusion-nonfree-rawhide-source too.
p/s: Edit the title from [rpmfusion-free-rawhide-source] to [rpmfusion-free-rawhide-sourceDISABLE] hack should make --enablerepo='*source' works, though so far i found it's unnecessary because i already enable all repos in first command.
I dont know if that is a answer because ls is written in C, but you can write a shell script to do a "ls" using the for loop:
for f in *;do echo $f; done
It is also usefull in some static shells...

- 6,628
which
if you're usingtcsh
or have aliasedwhich
to something liketype -p
orcommand -v
. See http://unix.stackexchange.com/questions/85249/why-not-use-which-what-to-use-then – cas May 17 '16 at 09:35https://github.com/wertarbyte/coreutils/blob/master/src/ls.c
:-)
. You may find online the source code. HNY. – Hastur Dec 29 '21 at 10:56