The parent wait process to complete the sort process before creating the ls process.
The sorting process must read its input before it is complete. And its input comes from ls, which will not start until wait is wait . Dead end.
You need to create both processes and then wait for both of them.
In addition, your file descriptors are not entirely correct. In this pair of calls:
close(0); dup2(fd[0], 0);
closing is redundant since dup2 automatically closes the existing fd 0, if any. You should do close(fd[0]) after ther dup2, so that you only have one file descriptor associated with this end of the pipe. And if you want to be really reliable, you should check wither fd[0]==0 already, in which case skip dup2 and close.
Apply all this to another dup2.
The question then becomes about the parent process in which the channel is open. I would say that you should close both ends of the channel in the parent after you passed them to the children, but you have this weird read from fd[0] after the last wait ... I don't know why this is. If the ls|sort pipeline works correctly, then the pipe will be empty, so there is nothing to read. In any case, you definitely need to close fd[1] in the parent element, otherwise the sorting process will not be completed, because the channel does not indicate EOF until all authors are closed.
After the weird read will be printf , which is likely to crash because the read buffer will not be '\0' -terminated.
And the point of using execlp is that it does a $PATH lookup for you, so you don't need to specify /bin/ . My first test run failed because my type is in /usr/bin/ . Why are hard ways when you don't need to?