51

cat < file prints the contents of file to stdout.

cat > file reads stdin until Ctrl+D is detected and the input text is written to file.

cat <> file, at least in my version of Bash, prints the contents of file happily (without error), but doesn't modify the file nor does it update the modification timestamp.

How does the Bash standard justify the seemingly ignored > in the third statement - and, more importantly, is it doing anything?

2 Answers2

57

Bash uses <> to create a read-write file descriptor:

The redirection operator

[n]<>word

causes the file whose name is the expansion of word to be opened for both reading and writing on file descriptor n, or on file descriptor 0 if n is not specified. If the file does not exist, it is created.

cat <> file opens file read-write and binds it to descriptor 0 (standard input). It's essentially equivalent to < file for any sensibly-written program, since nobody's likely to try writing to standard input ordinarily, but if one did it'd be able to.

You can write a simple C program to test that out directly - write(0, "hello", 6) will write hello into file via standard input.

<> should also work in any other POSIX-compliant shell with the same effect.

Michael Homer
  • 76,565
  • 1
    Writing... to stdin?... Is there any valid use case for this? – Qix - MONICA WAS MISTREATED Oct 27 '14 at 05:46
  • 3
    Off-hand, I can't think of any good one. Giving an explicit descriptor (4<>file) is useful, and I suppose 0 is as good a default as any when you leave it out. Reading from stdout isn't any better. – Michael Homer Oct 27 '14 at 05:49
  • 1
    While reading from stdout is generally not useful, 1<> the-file often is as it also does not truncate the-file. <> was introduced by the Bourne shell, so would work in any Bourne-like shell. – Stéphane Chazelas Oct 27 '14 at 07:20
  • 6
    <> is also useful on some systems (like Linux) to open named pipes without blocking until another process opens it for writing. – Stéphane Chazelas Oct 27 '14 at 10:59
  • 1
    @Qix: Well write(0, "Password: ", 10) is a good way to prompt for a password if you intend to prompt on anything kind of like a tty. I'm used to seeing it only on stderr but no reason in particular the same technique doesn't work on stdin. – Joshua Oct 27 '14 at 15:20
  • @Joshua, a couple of reasons why it may not be a good idea: tty devices don't have to be open in read+write mode. If you do that, you'd still want to check stdin is a terminal (and you might as well do a open("/dev/tty"). That prevents users from disabling it (by using 2> /dev/null) (with open("/dev/tty") as well though). – Stéphane Chazelas Oct 27 '14 at 15:44
  • I use some indirect connections for which open("/dev/tty") will fail because it's not really a tty but writing to stdin or stderr works just fine. – Joshua Oct 27 '14 at 15:57
  • So I suppose my question in response to all of this; is there anything special about FD 0/1/2? Or is it just convention that 0=stdin, 1=stdout, etc.? – Qix - MONICA WAS MISTREATED Oct 27 '14 at 16:12
  • @Qix: stdin, stdout and stderr file descriptors must be 0, 1 and 2 respectively when a program starts: http://pubs.opengroup.org/onlinepubs/009695399/functions/stdin.html – Michael Burr Oct 28 '14 at 06:34
  • 4
    @Qix - from the POSIX Rationale - The <> operator could be useful in writing an application that worked with several terminals, and occasionally wanted to start up a shell. That shell would in turn be unable to run applications that run from an ordinary controlling terminal unless it could make use of <> ... such as ... the pager more, which reads from standard error to get its commands, so standard input and standard output are both available for their usual usage. cat food | more - >/dev/tty03 2<>/dev/tty03 – mikeserv Oct 28 '14 at 11:26
  • <>file exists since the first Bourne Shell in 1978 so it is not a POSIX thing. UNIX opes the default file descriptors 0, 1, 2 for reading and writing and some programs expect to be able to read fom stderr or to write to stdin. This is why <> exists. – schily Sep 01 '15 at 12:29
  • @schily, however as noted by Sven Mascheck, it was initially undocumented and broken there (confirmed with v7 on a pdp11 emulator). Not fixed on SunOS before 5.6. BSD shells (based on ash) didn't have a <> until quite late for some. – Stéphane Chazelas May 30 '17 at 21:51
  • You seem to be correct, the files have been always opened either readonly or writeonly, so there was a bug in the interpreter part that did not implement all features understood by the parser. Fixed in 1997... – schily Jun 01 '17 at 09:11
51

<> file opens the file (on file descriptor 0 (stdin) by default, like <) in read+write mode without truncation and creating the file if it didn't exist beforehand.

That corresponds to the O_RDWR|O_CREAT flags passed to the open() system call. By contrast < is O_RDONLY and > is O_WRONLY|O_CREAT|O_TRUNC and >> O_WRONLY|O_CREAT|O_APPEND.

Having stdin writable is not often useful as applications usually don't write to their stdin. Applications usually don't expect to read and write on a file descriptor they receive on startup; they usually read from stdin (or a file descriptor they open themselves) and write to stdout or stderr (or a file descriptor they open themselves).

<> can have its uses:

  • You may prefer cat <> file over cat < file if you don't want the command to fail if file doesn't exist, but an empty file created instead.
  • The non-truncating aspect of <> makes it useful to overwrite files in place. In that case however, you generally don't use it on file descriptor 0:

    printf xxx 1<> file
    

    replaces the first 3 bytes of file with xxx.

  • On some systems like Linux, <> on a named pipe (FIFO) opens the named pipe without blocking (without waiting for some other process to open the other end), and ensures the pipe structure is left alive. For instance in:

    mkfifo pipe; sed 's/foo/bar/g' <> pipe
    

    sed handles incoming data from any number of other processes writing to it and never sees eof.

  • 2
    Note that on AT&T ksh93, <> defaults to 1<> (stdout) instead of 0<> (stdin). This is a POSIX compliance bug which I reported and will be fixed in the next release. https://github.com/att/ast/issues/75 But until current ksh93 versions fall out of use, you have to include the file descriptor number to use <> portably. – Martijn Dekker Apr 16 '18 at 03:47
  • @MartijnDekker, I know, I was the one telling you about it in the first place ;-). Note that it's only for ksh93t+ (where the behaviour changed) and above. – Stéphane Chazelas Sep 04 '18 at 16:27
  • 1
    What are (or were) the systems unlike Linux where mkfifo fifo; exec 3<>fifo would block? –  May 29 '19 at 15:44
  • @UncleBilly, I wouldn't remember. POSIX leaves it undefined if you open a fifo with O_RDWR. – Stéphane Chazelas Jan 22 '20 at 16:20