On my understanding, any command that reads from standard input (i.e keyboard), that it gets its input from a file.
$echo < text_content.txt
$
But echo command does not read and display the text_content.txt on the terminal. what's wrong here?
14.04command lineredirectscripts
On my understanding, any command that reads from standard input (i.e keyboard), that it gets its input from a file.
$echo < text_content.txt
$
But echo command does not read and display the text_content.txt on the terminal. what's wrong here?
You can't use file1 > file2
to copy file1
's contents to file2
because there's no command there. You have to issue some command.
exec
builtin). But they work by changing the source or target of actions that perform input-ouptut operations--that is, that read from or write to a file or device. You have to actually perform some action.file1 > file2
is a valid command, under some circumstances, but it doesn't copy file1
to file2
. Instead, as l0b0 says, it attempts to run file1
as a program or script, and--whether or not that succeeds--sends the standard output from running file1
into file2
. (If file2
is a regular file, it gets overwritten.)It's tempting to think something like <file1 >file2
would work. But it does not: when you leave off the command, no operation is performed. file1
is used as input and file2
is used as output... but since nothing is actually done, the output is empty, the only effect is to (a) create file2
if it didn't already exist, or (b) make file2
empty if it did:
ek@Ilex:~$ echo foobar > file1
ek@Ilex:~$ echo quux > file2
ek@Ilex:~$ cat file1
foobar
ek@Ilex:~$ cat file2
quux
ek@Ilex:~$ <file1 >file2
ek@Ilex:~$ cat file2
ek@Ilex:~$ file file1 file2
file1: ASCII text
file2: empty
cp file1 file2
.Assuming, that is, that file2
is a regular file (or doesn't exist but you want it to exist as a regular file after your command runs), you should probably simply use the cp
command.
As Sylvain Pineau says, you can use cat file1 > file2
as well. However, the cp
command is well-behaved when writing to a file that already exists: it overwrites the target with the source, but keeps the target's original permissions mask (and therefore is good even in situations where one might intuitively think cat file1 > file2
were needed):
ek@Ilex:~$ echo glockenspiel > file1
ek@Ilex:~$ echo flugelhorn > file2
ek@Ilex:~$ cat file1
glockenspiel
ek@Ilex:~$ cat file2
flugelhorn
ek@Ilex:~$ chmod +x file2
ek@Ilex:~$ ls -l file1 file2
-rw-rw-r-- 1 ek ek 13 Sep 16 03:28 file1
-rwxrwxr-x 1 ek ek 11 Sep 16 03:28 file2
ek@Ilex:~$ cp file1 file2
ek@Ilex:~$ cat file2
glockenspiel
ek@Ilex:~$ ls -l file1 file2
-rw-rw-r-- 1 ek ek 13 Sep 16 03:28 file1
-rwxrwxr-x 1 ek ek 13 Sep 16 03:28 file2
Both the cp
command and redirection with >
will overwrite the contents of a regular file, if it exists. But >>
redirection appends.
So if you want to append the contents of file1
to file2
instead of overwriting file2
with the contents of file1
, a redirection with >>
(not >
) is a good choice:
ek@Ilex:~$ echo 'Violets are blue.' > file1
ek@Ilex:~$ echo 'Roses are red.' > file2
ek@Ilex:~$ cat file1
Violets are blue.
ek@Ilex:~$ cat file2
Roses are red.
ek@Ilex:~$ cat file1 >> file2
ek@Ilex:~$ cat file2
Roses are red.
Violets are blue.
Input redirection (as in cat < file
) means the shell is opening the input file and writing its contents to the standard input of another process. Passing the file as an argument (as you do when running cat file
) means the program you are using (e.g. cat
) needs to open the file itself and read the contents.
Basically, command file
passes a file to command
while command < file
passes the contents of a file to command
. Yes, in cases like cat file
vs cat < file
there is no easily perceived difference in outcome, but but the two work in different ways.
To understand the difference, think of a young child and an adult. Both of them can drink water. However, the adult can open the tap and fill a glass (open the file and read its contents) while the child needs the water to be given to it directly (it can't open the file and can only process its contents).
Some programs, like cat
, are capable of taking a filename as input and then opening the file and doing their thing on it. That's why cat file
works. Other programs, however, don't have any knowledge of what files are or how to use them. All they know about is input streams (like the file's contents). For example, tr
:
$ cat file
foo
$ cat file | tr 'o' 'b' ## tr can read a stream
fbb
$ tr 'o' 'b' file ## tr can't deal with files
tr: extra operand ‘file’
Try 'tr --help' for more information.
$ tr 'o' 'b' < file ## input redirection!
fbb
Another example is ls
which can deal with files just fine, but ignores input streams:
$ ls
file1 file2
$ ls file1 ## lists only file1: ls takes file names as arguments
file1
$ ls < file1 ## ls ignores its standard input, this is the same as ls alone
file1 file2
Other programs can't deal with streams and instead require files:
$ rm < file ## fails, rm needs a file
rm: missing operand
Try 'rm --help' for more information.
$ rm file ## works, file is deleted
Some programs can deal with both opening files and reading input streams but behave in different ways with each. For example, wc
which, when given a file to open, prints the name of the file as well as the number of lines, words and characters:
$ wc file
1 1 4 file
But, if we just give it a stream, it has no way of knowing that this is coming from a specific file so no file name is printed:
$ wc < file
1 1 4
The md5sum
command behaves similarly:
$ md5sum file
17fd54512c91e3cd0f70fbaaa9a94d0d file
$ md5sum < file
17fd54512c91e3cd0f70fbaaa9a94d0d -
Note that in the first case the file name file
is shown while, in the second, "filename" is -
: standard input.
Now, if you want more gritty details, you can use strace
to see exactly what's going on:
strace -e trace=open,close,read,write wc file 2>strace1.txt
and
strace -e trace=open,close,read,write wc < file 2>strace2.txt
Those will have all the details of all open()
, close()
and read()
operations run by the process. What you want to see is that strace1.txt
(when the file was passed as an argument and not with input redirection) contains these lines:
open("file", O_RDONLY) = 3
read(3, "foo\n", 16384) = 4
Those mean that the file file
was opened and attached to the file descriptor 3
. Then, the string foo\n
was read from 3
. The equivalent part of the strace
output when using input redirection is:
read(0, "foo\n", 16384) = 4
There is no corresponding open()
call, instead the string foo\n
is being read from 0
, the standard input1.
1 By default, 0
is standard input, 1
is standard output and 2
is standard error. This, by the way, is why file
was opened as 3
, that was the next available one.
Best Answer
stdin and Commands
Effectively, yes. As I've discussed in my answer on What characterizes a file in Linux/Unix?, a file is any object on which you can execute standard operations such as
read()
,open()
,write()
,close()
.stdin
being represented via file descriptor 0 is effectively a file in that regard, and any command/process in Linux gets 3 standard file descriptors - stdin,stdout,stderr - when that process is started. What are the actual files behind those file descriptors ? The command doesn't care and shouldn't, as long as it can do operations on it.Now, command is free to do what it will with those file descriptors1. In case of
echo
it only deals withstdout
and doesn't perform any action onstdin
at all. So there's nothing wrong with the command itself.<
redirection willopen()
the filetext_content.txt
for reading, and it still will assign file descriptor ( for example 3 ) returned fromopen()
call to file descriptor 0, and if a command is concerned with stdin - it will read from file descriptor 0 as if nothing happened.In fact, you will see that in action, if you runstrace -f -e dup2,write,openat bash -c 'echo < text_content.txt
Notice the
dup2()
system call. That's how file descriptor 3 (the file) is assigned/redirected. In sort ofcp original copy
syntax,dup2(3,0)
makes a copy of file descriptor 3 to file descriptor 0, and they point to same file.Notice also that
write()
output a newline to file descriptor1
. That's the default behavior. If we dostrace -f -e dup2,write,openat bash -c 'echo FOO < /etc/passwd'
here's what we will seeSo again, nothing wrong here - redirections are performed properly and
echo
does its job of writing stuff tostdout
which is file descriptor 1.How to actually read a file
Now, lets address something else. How can we read a file in shell ? Well, for that there exists
cat
command, which accepts arguments, so you can do justcat file.txt
. Can you docat < file.txt
? Sure. But that means shell will have to do thatdup2()
call, whereascat file.txt
doesn't - so there's less unnecessary syscalls is what I'm saying.In complex cases, such as when you need to perform an action on each line of the file, you would do
Now, for the whole loop file descriptor
0
will be a copy of whatever file descriptor is returned from opening/etc/passwd
. Of course, if you can usecat
or another specific command to the job of reading a file - do that. Shell is slow method and has a lot of pitfalls. See also, Why is using a shell loop to process text considered bad practice?1. Some applications still might care about what they can do with stdin or detect if stdin is a file or pipeline. When
stdin
file descriptor is assigned as read-end of pipeline (which is also a file descriptor) the output is not seekable (meaning that application written in C or another language cannot useseek()
syscall to quickly navigate to specific byte offset in a file). A good example of that is given in the question titled What is the difference between “cat file | ./binary” and “./binary < file”?Sidenote: on Linux it's not exactly keyboard from where stdin gets its input. If you do
you will see in the output that it is
/dev/pts/0
terminal device to whichstdin
points initially. The terminal device then interfaces with keyboard, or it could as well be serial cable.Additionally, if file is not very large, you could take advantage of
bash
'smapfile
builtin to read lines into array: