Memra

The shell re-points a slot before the command runs

◈ 9 cards

Use <, > and >> correctly, and explain why the command they act on is unchanged, unaware, and — for commands like tr — otherwise unusable on a file.

One action, at one moment

Redirection is a single thing that happens at a single moment: before the command runs, the shell opens a file and points one of the three standard descriptors at it. Then it hands over and stays out of the way. The program that starts is byte-for-byte the same program it would have been without the redirection, and nothing inside it can tell the difference.

  • cmd < file — open file for reading, point descriptor 0 at it.
  • cmd > file — create file, or truncate it to zero length if it exists, and point descriptor 1 at it.
  • cmd >> file — open file for appending, point descriptor 1 at it.

Each operator may be written with its descriptor number spelled out: 0< file is < file, and 1> file is > file. Nobody writes the number for 0 or 1 in practice, but you must be able to read it, because the moment stderr enters the picture the numbers stop being optional.

Worked example: sort < students > sorted

Without the redirections, sort would inherit three descriptors all pointing at the terminal. The shell processes the two operators first, so at the instant sort starts, slot 0 points at students, slot 1 points at sorted, and slot 2 still points at the terminal. sort reads what it is handed, sorts it, writes what it produces, and reports any trouble to your screen — and it opened neither file.

Now compare sort students > sorted. Same visible result, different mechanism. students is now an argument: the shell passes the word through untouched, sort opens it itself, and descriptor 0 stays attached to the keyboard the whole time. Two roads, one destination.

Where the two roads stop being equivalent

The difference stops being academic the moment a command takes no filename argument at all. tr is the standard case: it translates or squeezes characters and it reads only from stdin. tr -s ' ' Phones is an error — tr has nowhere to put that word. tr -s ' ' < Phones works, because the shell did the opening and tr never had to be told.

A second case is cat < a > b against cp a b. If b does not exist, the two do the same visible thing. If b does exist they differ, and the difference is about identity: the redirection form opens the existing b, truncates it to zero length, and writes new data into it, so afterwards it is the same inode — same permission bits, same owner, same link count, same hard links still pointing at it. cp is free to replace the destination attributes from the source. cat > preserves the destination file; cp overwrites it. When a file has a carefully set mode, or two hard links pointing at it, that distinction is the whole ball game.

Appending, protection, and the bit bucket

> destroys silently and asks nothing. Bash offers set -o noclobber, which makes > refuse to overwrite an existing file, and >| to override the protection for one command; put the set line in your startup file to make it permanent. (The C shell spells the same idea set noclobber and overrides it with >!.) >> is the other answer: it opens at the end and adds, so nothing already in the file is at risk.

And finally /dev/null, which is used constantly and defined nowhere. It is a real character-special file with a genuinely useful pair of behaviours: every write to it is discarded, and every read from it returns end-of-file immediately. It has no size and never grows. So cmd > /dev/null means run this and throw the output away — what you want when you care only whether the command succeeded, or when you are timing it and do not want to measure your terminal.

012beforettyttyttyafterstudentssortedttysort < students > sorted — the shell opened both files.
Only the two accented cells changed, and `sort` is the identical program in both rows. Slot 2 never moved, which is why a missing input file still complains to your screen.
NORMAL ~/memra/learn/comp-325/input-and-output-redirection utf-8 LF