cat is a neat command to concatenate files on Unix
(please see this post). Let’s see some examples:
# cat 1.txt
1
# cat 2.txt
2
# cat 1.txt 2.txt > new.txt
# cat new.txt
1
2
# cat 1.txt 2.txt >> new.txt
# cat new.txt
1
2
1
2
Please notice if the output file is also the input file, the input file content will be truncated first:
# cat 1.txt
1
# cat 2.txt
2
# cat 1.txt 2.txt > 1.txt
# cat 1.txt
2
# cat 2.txt
2
So the correct appending file method is using >>
:
# cat 1.txt
1
# cat 2.txt
2
# cat 2.txt >> 1.txt
# cat 1.txt
1
2
# cat 2.txt
2
Check the implementation of cat in OpenBSD
, and the core parts are iterating files and reading content from them:
(1) Iterate every file (raw_args):
void
raw_args(char **argv)
{
int fd;
fd = fileno(stdin);
filename = "stdin";
do {
if (*argv) {
if (!strcmp(*argv, "-"))
fd = fileno(stdin);
else if ((fd = open(*argv, O_RDONLY, 0)) < 0) {
warn("%s", *argv);
rval = 1;
++argv;
continue;
}
filename = *argv++;
}
raw_cat(fd);
if (fd != fileno(stdin))
(void)close(fd);
} while (*argv);
}
You need to pay attention that cat
use -
to identify stdin
.
(2) Read content from every file (raw_cat):
void
raw_cat(int rfd)
{
int wfd;
ssize_t nr, nw, off;
static size_t bsize;
static char *buf = NULL;
struct stat sbuf;
wfd = fileno(stdout);
if (buf == NULL) {
if (fstat(wfd, &sbuf))
err(1, "stdout");
bsize = MAXIMUM(sbuf.st_blksize, BUFSIZ);
if ((buf = malloc(bsize)) == NULL)
err(1, "malloc");
}
while ((nr = read(rfd, buf, bsize)) != -1 && nr != 0)
for (off = 0; nr; nr -= nw, off += nw)
if ((nw = write(wfd, buf + off, (size_t)nr)) == 0 ||
nw == -1)
err(1, "stdout");
if (nr < 0) {
warn("%s", filename);
rval = 1;
}
}
a) When reading the first file, the cat
command uses fstat to get the st_blksize
attribute of stdout
which is “optimal blocksize for I/O”, then allocates the memory:
......
if (buf == NULL) {
if (fstat(wfd, &sbuf))
err(1, "stdout");
bsize = MAXIMUM(sbuf.st_blksize, BUFSIZ);
if ((buf = malloc(bsize)) == NULL)
err(1, "malloc");
}
......
b) Read the content of file and write it into stdout
:
......
while ((nr = read(rfd, buf, bsize)) != -1 && nr != 0)
for (off = 0; nr; nr -= nw, off += nw)
if ((nw = write(wfd, buf + off, (size_t)nr)) == 0 ||
nw == -1)
err(1, "stdout");
......
When read returns 0
, it means reaching the end of file. If write doesn’t return the number you want to write, it is also considered as an error.