By convention, a filter program reads its text from the standard input device and writes the results of its operations to the standard output device. When it reaches the end of the input stream, the filter simply terminates. As a result, filters are both flexible and simple.
Filter programs are flexible because they do not know, and do not care about, the source of the data they process or the destination of their output. Thus, any character device that has a logical name within the system (CON, AUX, COM1, COM2, PRN, LPT1, LPT2, LPT3, and so on), any file on any block device (local or network) known to the system, or any other program can supply a filter's input or accept its output. If necessary, you can concatenate several functionally simple filters with pipes to perform very complex operations.
Although flexible, filters are also simple because they rely on their parent processes to supply standard input and standard output handles that have already been appropriately redirected. The parent must open or create any necessary files, check the validity of logical character-device names, and load and execute the preceding or following process in a pipe. The filter concerns itself only with the transformation it applies to the data.