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 historyreturn "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 infishshell)In csh/tcsh shell, you should use
where historybecause no suchtypecommand. 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 fromrepoqueryabove 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
whichif you're usingtcshor have aliasedwhichto something liketype -porcommand -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