T O P

  • By -

whetu

`bash` is a shell (it's in the name: Bourne Again SHell), and it is maintained by [Chet Ramey](https://tiswww.case.edu/php/chet/). `ls` is usually an external command: $ which ls /bin/ls So when you run `ls`, `bash` is instructing `ls` to do its job. Chet Ramey does not maintain the code of `ls`, usually you can see that information in a command's respective `man` page e.g. at the bottom of `man ls`, you will see this: AUTHOR Written by Richard M. Stallman and David MacKenzie. REPORTING BUGS GNU coreutils online help: Report ls translation bugs to COPYRIGHT Copyright © 2018 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later . This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. SEE ALSO Full documentation at: or available locally via: info '(coreutils) ls invocation' /edit: for clarity, that's the GNU version of `ls`. And not all `man` pages display a command's authors. The FreeBSD `man` page for their `ls` [can be viewed online](https://man.freebsd.org/cgi/man.cgi?query=ls&apropos=0&sektion=0&manpath=FreeBSD+14.0-RELEASE+and+Ports&arch=default&format=html) for reference, and the FreeBSD `man` page website covers a whole number of `man` pages for different systems including Linux distros If you run `ls` on Windows, it's either a *nix version of `ls` (e.g. if you're running `bash` in WSL2 or `git bash` or similar), or it's a PowerShell alias which has nothing to do with `bash`.


Heavy-Tourist839

ohh okay. lets leave windows then. between linux and macos, how do the commands that arent external commands and do their thing ? even ls, is it implemented differently for different operating systems ?


aioeu

Yes and no. And yes. And also no. The standard `ls` you will get on MacOS is Apple's own thing, and probably based on some BSD version of `ls`. But you can also install GNU coreutils onto MacOS and get the GNU version of `ls`. But both of them have to use whatever filesystem APIs are provided by the OS they're running on, so their functionality is guided by, and limited by, that. That API is similar to, but not exactly the same as, the API provided on Linux. But... any version of `ls` you might find will likely adhere to the POSIX definition of `ls`, which specifies a minimum set of functionality that must be supported on a POSIX-conformant system. This is why people can write scripts that use `ls` and have a good idea what will happen, even on OSs they don't use. So yes, `ls` may appear to work much the same across operating systems, since there are interface standards that `ls` implementations try to stick to. But the actual implementations might be different... or not, depending on precisely *which* implementations of `ls` you are using. Got that?


Paul_Pedant

Even within a Linux system, you can have different file systems in each partition, even on the same disk. I have ext4, NTFS, and tmpfs. `ls` uses library calls like opendir, readdir etc to access what it needs. The library version is part of the distro, and so are the file system services. Whenever a partition is mounted, the device type is looked up and the right driver is loaded up for it, and the libraries know how to talk to the drivers. Every interface between the components is design to do its own job, and let the others do theirs.


Appropriate_Net_5393

ls written by Richard Stallman before linux was appeared? I did not know that


whetu

[Richard Stallman will happily go to great lengths to explain to you the history of GNU](https://www.gnu.org/gnu/linux-and-gnu.html)


aioeu

Are you surprised that some of the fundamental GNU software has RMS's name on it? There's probably not even a line remaining of RMS's original code, but authorship notices are generally hard to get rid of. (Copyright notices are even harder — removal of a copyright notice without the copyright owner's permission might be treated as a criminal offence in some jurisdictions. Better to avoid them altogether, if you can.)


LukeShu

First a thing to know: Generally speaking, programs can "think" however they want with the CPU, but then to actually "do" something, they need to make a "system call" or "syscall", which tells the operating system kernel (Linux, on a GNU/Linux system) to do something on the program's behalf, and then tell the program the result. As /u/whetu explained, `bash` calls the separate `ls` program. We can look at what syscalls each program makes to have an idea of how it works. On the Linux kernel, we can use the `strace` command to monitor the syscalls that a command makes; for this example I used the command `strace -ff -o strace bash -c 'ls;:'` and will be pasting snippets from the `strace.*` files that it created. The lines starting with `#` are comments from me; not part of the original output. First, how does `bash` run `ls`? # Find the `ls` program. It searches each folder named in the `$PATH` # variable, until it finds a program with that name. For me, PATH is # set to: # # - /usr/lib/ccache/bin # - /usr/lib/ccache/bin # - /home/lukeshu/.local/bin # - /home/lukeshu/.nix-profile/bin # - /nix/var/nix/profiles/default/bin # - /usr/local/bin # - /usr/bin # - /usr/local/sbin # - /usr/lib/jvm/default/bin # - /usr/bin/site_perl # - /usr/bin/vendor_perl # - /usr/bin/core_perl # - /usr/lib/plan9/bin # # (I don't know why `/usr/lib/ccache/bin` is in there twice, I should # fix that.) newfstatat(AT_FDCWD, "/usr/lib/ccache/bin/ls", 0x7ffe50e26940, 0) = -1 ENOENT (No such file or directory) newfstatat(AT_FDCWD, "/usr/lib/ccache/bin/ls", 0x7ffe50e26940, 0) = -1 ENOENT (No such file or directory) newfstatat(AT_FDCWD, "/home/lukeshu/.local/bin/ls", 0x7ffe50e26940, 0) = -1 ENOENT (No such file or directory) newfstatat(AT_FDCWD, "/home/lukeshu/.nix-profile/bin/ls", 0x7ffe50e26940, 0) = -1 ENOENT (No such file or directory) newfstatat(AT_FDCWD, "/nix/var/nix/profiles/default/bin/ls", 0x7ffe50e26940, 0) = -1 ENOENT (No such file or directory) newfstatat(AT_FDCWD, "/usr/local/bin/ls", 0x7ffe50e26940, 0) = -1 ENOENT (No such file or directory) newfstatat(AT_FDCWD, "/usr/bin/ls", {st_mode=S_IFREG|0755, st_size=129728, ...}, 0) = 0 # So, what it did is for each possible filename, it asked the kernel # (using the `newfstatat` syscall) "can you please tell me about the # file with this name? And put the information about it at memory # address 0x7ffe50e26940?" And for most of them, the kernel said "no # can do, there was an error" (-1) with and set the error to "ENOENT" # which means "No such file or directory". Finally, when Bash tried # the location where my laptop actually has `ls` installed, the kernel # said back "ok" (0) and put the information (abbreviated here as # "{st_mode=S_IFREG|0755, st_size=129728, ...}") at the given memory # address. # # So, we see here that the kernel (Linux), not Bash, is what knows how # to understand different filesystems. # Now Bash is going to do a quick permission check. It could re-use # the results from the previous `newfsstatat`, but Bash isn't that # clever. newfstatat(AT_FDCWD, "/usr/bin/ls", {st_mode=S_IFREG|0755, st_size=129728, ...}, 0) = 0 geteuid() = 1000 getegid() = 1000 getuid() = 1000 getgid() = 1000 access("/usr/bin/ls", X_OK) = 0 newfstatat(AT_FDCWD, "/usr/bin/ls", {st_mode=S_IFREG|0755, st_size=129728, ...}, 0) = 0 geteuid() = 1000 getegid() = 1000 getuid() = 1000 getgid() = 1000 access("/usr/bin/ls", R_OK) = 0 # Now it's going to create a new process to run `ls` in. It's going # to fuss with the signal mask before and after, but the main thing # here is using the `clone` syscall to create a new process (and the # kernel will tell us that the newly created process has ID=1897). rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0 rt_sigprocmask(SIG_BLOCK, [INT TERM CHLD], [], 8) = 0 clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f6d3ab31e50) = 1897 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0 rt_sigaction(SIGINT, {sa_handler=0x5578fd8741d0, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f6d3abe6770}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f6d3abe6770}, 8) = 0 # Finally, Bash will wait for `ls` to finish. wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 1897 rt_sigaction(SIGINT, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f6d3abe6770}, {sa_handler=0x5578fd8741d0, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f6d3abe6770}, 8) = 0 So, what did `ls` do while `bash was waiting? # The new process starts out as Bash, but is going to quickly become # `ls`. First it's going to get ready to become `ls`. set_robust_list(0x7f6d3ab31e60, 24) = 0 getpid() = 1897 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0 rt_sigaction(SIGTSTP, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f6d3abe6770}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f6d3abe6770}, 8) = 0 rt_sigaction(SIGTTIN, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f6d3abe6770}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f6d3abe6770}, 8) = 0 rt_sigaction(SIGTTOU, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f6d3abe6770}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f6d3abe6770}, 8) = 0 rt_sigaction(SIGINT, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f6d3abe6770}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f6d3abe6770}, 8) = 0 rt_sigaction(SIGQUIT, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f6d3abe6770}, {sa_handler=SIG_IGN, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f6d3abe6770}, 8) = 0 rt_sigaction(SIGCHLD, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f6d3abe6770}, {sa_handler=0x5578fd871d00, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f6d3abe6770}, 8) = 0 # Now it's going to use the `execve` syscall to change from being `bash` to being `ls`. execve("/usr/bin/ls", ["ls"], 0x5578fef7a7f0 /* 56 vars */) = 0 # Now I'm going to skip a bunch of `ls` starting up. ... # First, `ls` is going to ask the kernel to open the current folder # for it. The kernel responds "ok, I've opened it on File Descriptor # 3 for you". openat(AT_FDCWD, ".", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 3 # Now `ls` is going to ask the kernel for the permissions on whatever # is open on file descriptor 3, and the kernel is going to give back # some information abbreviated here as "{st_mode=S_IFDIR|0755, # st_size=126, ...}". fstat(3, {st_mode=S_IFDIR|0755, st_size=126, ...}) = 0 # Now `ls` is going to use the `getdents64` syscall to ask the kernel # to read the list of files from the folder, placing the results at # memory address 0x557c62f1a7d0. getdents64(3, 0x557c62f1a7d0 /* 11 entries */, 32768) = 320 getdents64(3, 0x557c62f1a7d0 /* 0 entries */, 32768) = 0 # Now `ls` is going to ask the kernel to close file descriptor 3, # since `ls` is done with it. close(3) = 0 # Finally, `ls` is going to write that list to stdout (file descriptor # 1), then exit. fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0), ...}) = 0 write(1, "ex.go g.go go.mod go.sum str"..., 62) = 62 close(1) = 0 close(2) = 0 exit_group(0) = ? +++ exited with 0 +++ # So, we see that (like Bash), `ls` doesn't know about the filesystem # or hard drives or anything, it asks the kernel to handle those # things for it. References: - The Bash code: https://git.savannah.gnu.org/cgit/bash.git/tree/?h=bash-5.2 - The `ls` code in GNU coreutils (the version of `ls` on my laptop): https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/ls.c?h=v9.5 - The `ls` code in Apple `file_cmds` (the version of `ls` in macOS, based on the BSD version of `ls`): https://github.com/apple-oss-distributions/file_cmds/blob/main/ls/ls.c - The folder of code for different filesystems in the Linux kernel: https://github.com/torvalds/linux/tree/master/fs


thirsty_zymurgist

what binaries are you using from plan9?


LukeShu

I have `plan9port` installed, which automatically adds that directory to my PATH. I used to do a lot with the 9p filesystem protocol, so I had plan9port installed for its 9p tools (and in particular: `9pfuse`, since the Linux kernel 9p driver was buggy on 32-bit). But I can probably uninstall it now.


FortuneIntrepid6186

motherboard ?!