Linux Essentials
File handles (file descriptors) in Linux
File descriptor is integer that uniquely identifies an open file of the process.
in simple words, when you open a file, the operating system creates an entry to represent that file and store the information about that opened file. So if there are 100 files opened in your OS then there will be 100 entries in OS (somewhere in kernel). These entries are represented by integers like (…100, 101, 102….). This entry number is the file descriptor. So it is just an integer number that uniquely represents an opened file for the process. If your process opens 10 files then your Process table will have 10 entries for file descriptors.
Similarly, when you open a network socket, it is also represented by an integer and it is called Socket Descriptor.
int main()
{
pid_t pid = getpid();
printf("pid = %d\n", pid);
// int open(const char *pathname, int flags);
int file_fd = open("file.txt", O_RDONLY); // open file file_fd = 3
// FILE *fopen(const char *pathname, const char *mode);
FILE *fp = fopen("file.txt", "r"); // open file file_fd = 4
// int socket(int domain, int type, int protocol);
int sock_fd = socket(AF_INET, SOCK_STREAM, 0); // open socket sock_fd = 5
int b;
scanf("%d", &b);
return 0;
}
// Run the code to get the PID = 12345 for example
$ tty
/dev/pts/0
$ ls -la /proc/12345/fd
/proc/12345/fd:
total 0
dr-x------ 2 user user 0 Oct 2 20:53 .
dr-xr-xr-x 9 user user 0 Oct 2 20:53 ..
lrwx------ 1 user user 64 Oct 2 20:53 0 -> /dev/pts/0
lrwx------ 1 user user 64 Oct 2 20:53 1 -> /dev/pts/0
lrwx------ 1 user user 64 Oct 2 20:53 2 -> /dev/pts/0
lr-x------ 1 user user 64 Oct 2 20:53 3 -> /tmp/file.txt
lr-x------ 1 user user 64 Oct 2 20:53 4 -> /tmp/file.txt
lrwx------ 1 user user 64 Oct 2 20:53 5 -> 'socket:[273926]'
Ex2
int main(){
// ssize_t read(int fd, void *buf, size_t count);
char buf[5];
ssize_t len = read(0, buf, 5);
// KINDA similar to
scanf("%5s", buf);
return 0;
}
When any process starts, then that process file descriptors table’s fd 0, 1, 2 open automatically.
0 –> stdout
1 –> stdin
2 –> stderr
So (stdout, stdin, stderr) are file objects (FILE *) that opened by default when the process starts,hence, we can read from them or write on them … see below !!
int main(){
// int fprintf(FILE *stream, const char *format, ...);
fprintf(stdout, "hello world\n");
fprintf(stderr, "erorr world\n");
// int fscanf(FILE *stream, const char *format, ...);
int a;
fscanf(stdin, "%d", &a);
printf("%d\n", a);
return 0;
}
Input-Output Redirection
| redirect the output of the first command can be used as the input of the second command.
< 0< redirect the input to the first command from a file
> 1> redirect the output of first command to a file
2> redirect the stderr of first command to a file
&> redirect the stdout & stderr of first command to a file
<> This operator is used for specified files as both standard input and standard output.
>& redirects the output of one file to another.
<& redirects the input of one file to another.
2>&1 redirects standard error to standard output.
1>&2 This operator is used for redirecting standard output to standard error
$PATH & shell built-in commands
shell built-in commands list :
: | . | break | cd | continue | echo | eval | exec | exit | export | hash | pwd | read | readonly | return | set | shift | test | times | trap | type | ulimit | umask | unset | wait | printf
# printf | echo | eval | exec | pwd | cd ==> the most interesting
# read ??
$ echo "HIIII" | read VAR1
$ echo $VAR1
HIIII
Does a
shell built-in commandactually run the program from $PATH env variable ?
The shell knows what utilities are built-ins.
The bash shell finds the command using these steps (once alias expansion has been performed):
- If the command contains no slashes
- If the command is a shell function, that function is called.
- Otherwise, if it corresponds to a built-in command, that command is used.
- Otherwise, the shell searches $PATH for an executable with that name and executes it if found.
- If the command contains slashes, that named file is executed (e.g. /bin/cat)
during the development of glibc 2.24 caused the current working directory to be dropped altogether from the default search PATH.
in another words ::
linux does not look in the current directory to see whether a specific command is available from that directory.
if you’re the only user on the machine and you’re going to add . to your PATH, I suggest appending it to the end of the list (export PATH=$PATH:.). At least you won’t override system-wide binaries this way .
echo is not very portable (even Bash's echo may behave differently on different OSes which may use different default options when compiling Bash). You can use printf