89

I have a file named my_file.txt whose content is just the string Hello. How could I redirect its content to the command echo?

I know I have the commands less, cat, more... but I need to do it with echo.

I tried this:

$ cat my_file.txt | echo

and also this:

$ echo < my_file.txt

But in both cases it appears only a blank in the stdout, not the content of my_file.txt.

How could I do that?

Seanny123
  • 111
danielmbcn
  • 1,085
  • 13
    Why do you insist on using echo? – Kevin Feb 04 '13 at 14:53
  • 12
    @Kevin, presumably eventually he wants to expand the ANSI C escape sequences (\n, b...) in the file, which would be a valid usage of echo (at least a standard echo). Or presumably he wants to understand why it doesn't work that way. In any case, no reason to downvote IMO. – Stéphane Chazelas Apr 02 '13 at 06:38
  • 9
    I came here because I was curious about why it doesn't work when I do echo < file.txt. Seems a reasonable question to have at SE, the answer was very helpful. +1 from me. – neuronet Jun 16 '16 at 15:01
  • I have a use case for this. It looks to me like echo and printf don't handle stdin, unfortunately, that's why I'm looking, but I've got some text output from a command chain with embedded "\n" characters and I'd like to just print it properly. Is it so hard to imagine a use case where someone wants to print text to console? – NeilG Aug 23 '23 at 06:01

6 Answers6

117

You can redirect all you want to echo but it won't do anything with it. echo doesn't read its standard input. All it does is write to standard output its arguments separated by a space character and terminated by a newline character (and with some echo implementations with some escape sequences in them expanded and/or arguments starting with - possibly treated as options).

If you want echo to display the content of a file, you have to pass that content as an argument to echo. Something like:

echo "$(cat my_file.txt)"

Note that $(...) strips the trailing newline characters from the output of that cat command, and echo adds one back.

Also note that except with zsh, you can't pass NUL characters in the arguments of a command, so that above will typically not work with binary files. yash will also remove bytes that don't form part of valid characters.

If the reason for wanting to do that is because you want echo to expand the \n, \b, \0351... escape sequences in the file (as UNIX conformant echo implementations do, but not all), then you'd rather use printf instead:

printf '%b\n' "$(cat my_file.txt)"

Contrary to echo, that one is portable and won't have problems if the content of the file starts with -.


As an alternative to $(cat file), with ksh, zsh and bash, one can also do: $(<file). That's a special operator whereby the shell as opposed to cat reads the content of the file to make up the expansion. It still strips the trailing newlines and chokes on NUL bytes except in zsh. In bash, that still forks an extra process. Also note that one difference is that you won't get any error if trying to read a file of type directory that way. Also, while $(< file) is special, $(< file; other command) is not (in zsh, when not emulating other shell, that would still expand the content of the file, by running the implicit $READNULLCMD command (typically a pager)).

  • But how to repeat standard input to standard output while interpreting special characters? – NeilG Aug 23 '23 at 06:03
  • @NeilG This seems like a related but different question. If no other question on the site handles this, I would suggest you ask a brand new question about it, including example data. – Kusalananda Aug 23 '23 at 07:03
  • I was thinking about it when I found the answer among those for this question @Kusalananda. Take a look. – NeilG Aug 23 '23 at 13:08
  • @NeilG, well, just printf '%b\n' "$(cat)" or for more than once: var=$(cat); printf '%b\n' "$var" "$var" "$var"..., or use the printf '%1$b\n%1$b...\n' "$(cat)" supported by many printf implementations. – Stéphane Chazelas Aug 23 '23 at 15:56
  • Lol, @StéphaneChazelas, thanks but I think I'll just stick with | xargs echo -e! GNU bash, version 5.1.16(1)-release. But that's an interesting use of cat. I'll try some variations on that too. – NeilG Aug 24 '23 at 00:31
  • @NeilG, as already noted, you can't use xargs unless you use the -0 GNU extension as xargs mangles quotes, whitespaces and backslash. It will also fail for strings like -nene and for long strings as there's a limit (low on some systems) on the size of the arguments that can be passed to an external command. – Stéphane Chazelas Aug 24 '23 at 05:31
  • Good to know @StéphaneChazelas . The command line length limit was what was bothering me most. I'm actually using $(cat) now too, as it's cooler and more conventional at the same time. – NeilG Aug 24 '23 at 06:38
17

Adding to other answers, you can also try input redirection instead of cat

echo "$(<my_file.txt)"

or

echo "`<my_file.txt`"

$(...) and `...` perform the same thing, i.e command substitution. It runs commands inside of it's scope and gives the output to the shell as if it was given as an input by the user. The only difference between these two is that $(...) can be nested simply and `...` needs to be escaped to be nested.

The quotes around command substitution preserve whitespaces

  • 2
    \...`` can be recursive but with a much more awkward syntax. You must also watch out for backslashes inside them (even inside single quotes), and " if quoted. In any case with either syntax, they should be quoted when in list context unless you want split+glob to be applied to them. Note that $(<file) is a non-standard ksh extension (also available in bash and zsh) – Stéphane Chazelas Mar 26 '21 at 07:39
  • 1
    Thank you! This is extremely helpful for debuging when your system SSD/HDD crashed and cat can't be loaded any more.

    For example on a Raspberry Pi that browned out due to insufficent voltage cat /sys/devices/platform/soc/soc:firmware/get_throttled yields bash: cat: command not found But the shell built-in echo \</sys/devices/platform/soc/soc:firmware/get_throttled`` still works and returns the desired debug code.

    – user643011 Oct 12 '21 at 01:28
6

As it was said,

If you want echo to display the content of a file, you have to pass that content as an argument to echo

For instance, you can do it like this with xargs (considering that you need to enable interpretation of backslash escapes):

$ cat my_file.txt | xargs echo -e

Or simply what you've asked (whithout interpretation of escapes sequences):

$ cat my_file.txt | xargs echo
kenichi
  • 260
  • 1
    Note however that when using xargs echo (same as xargs btw as echo is the default command), it's the echo command from the file system that will be run, not the shell's builtin echo command. Also note that xargs expects its input in a very specific format (where blanks, newlines, quotes, backslashes have special meaning). cat doesn't make much sense here as there's only one file to concatenate (same as < file.txt xargs echo. Note that Unix compliant echos do backslash escape expansion by default and output -e upon echo -e. – Stéphane Chazelas May 08 '17 at 08:39
  • 1
    Of course! xargs! Yes! command | command | command | xargs echo -e will take the standard output of your command chain and pipe it to xargs which will then arrange it as parameters to echo which will print the special characters! Thank you! – NeilG Aug 23 '23 at 06:06
  • @NeilG But note that xargs does special processing of quotes, and that not all external implementations of echo may have the non-standard -e option. – Kusalananda Aug 23 '23 at 07:19
2

Simple answer: you can't. echo (be it shell built-in or regular binary) doesn't process its standard input - it is one way only.

peterph
  • 30,838
  • Question is, why would you actually want to do this? – peterph Feb 04 '13 at 19:14
  • please refer to multiple comments and text in the other answers to find out why. – NeilG Aug 23 '23 at 06:06
  • Yes you can! Prefix echo with xargs! Well, I know, strictly you can't; but that has a similar effect that may solve some problem scenarios that need this behaviour. – NeilG Aug 23 '23 at 06:07
  • @NeilG, with your suggestion echo still does not read from stdin. It's the xargs command reading stdin and transcribing it to command line arguments that are passed to one or more instances of echo – Chris Davies Sep 22 '23 at 08:44
  • Thanks for the explanation, @roaima (that's why I said "strictly you can't" and "similar effect"). – NeilG Sep 22 '23 at 10:32
2

When it comes to redirection, it is important to note that Linux commands can interpret the argument you pass to them differently, thus leading to different behaviors. The crucial part is to understand that

  • Some commands expect a path to a file and put the contents of that file within their STDIN. After that, such commands read from there, process it, and produce their output. For example, cat does so.
  • Other commands put nothing within their STDIN and don't read from there. In other words, the STDIN is simply ignored. Instead, they directly read from theirs arguments and generate the output.

Therefore, some commands can depend entirely on what is within their STDIN, while others don't give a damn for what is there. That is important for redirection because if you redirect a STDOUT to the STDIN of a command that ignores it, you won't get the desired behavior.

If you run

cat

you will notice as if cat is hung, waiting for something. It happens because, if no content is passed to within the STDIN, the standard input is, by default, attached to the keyboard. In other words, cat is waiting us to type any content and send to it by pressing enter.

On the other hand,

echo

simply leaves the command because it doesn't reads from the STDIN. As it had no argument, there is nothing to the printed.

Therefore, if you type

echo < my_file.txt

Normally, echo would treat my_file.txt as an argument and would simply produce my_file.txt in the STDOUT, but < changes how my_file.txt is treated by the shell. Now, instead of passing my_file.txt as an argument to echo, the shell puts the content of ./my_file.txt within the STDIN of echo. As we saw before, it doesn't print anything because echo doesn't read from the standard input.

If you really want that echo reads from STDIN, you must use xargs, that is,

xargs echo < my_file.txt
  • < makes my_file.txt not an argument, but a path to a file whose content is put within STDIN.
  • xargs runs the command given to it (echo) with the arguments read from STDIN.

Now it will prints out "Hello".

Similarly,

cat my_file.txt | xargs echo

also works:

  • cat get the content of ./my_file.txt and put it within its STDIN and print this content in its STDOUT.
  • The pipe | takes the STDOUT of cat and put it within the STDIN of echo instead of printing this STDOUT on the terminal (the default behavior).
  • xargs runs the command given to it (echo) with the arguments read from STDIN.
  • 1
    Note that both facts have been mentioned in other answers already (the fact that echo does not read its standard input, and that you can use xargs). Also note that echo happens to be the default utility that xargs executes (i.e, shorter: xargs <my_file.txt). There are also issues with using xargs that you don't mention, which includes the processing of quotes and whitespace. – Kusalananda Jan 20 '23 at 15:32
  • @Kusalananda although these facts have been mentioned previously, none of answers here gave my solution (Stéphane has reached close it, though). That is right that the usage of echo is optional. I preferred to leave it for the sake of clarity. The issues concerning xargs are leave out since it is out of the scope of the question. – Rubem Pacelli Jan 20 '23 at 15:41
  • "xargs forces echo to read from the STDIN rather than its arguments." No, it doesn't. xargs itself reads stdin, parses the input and runs the command given with the input as arguments. – muru Sep 21 '23 at 23:41
  • Also when you say none the answers have your solution, if you mean using xargs is the solution, it was already posted 7 years ago: https://unix.stackexchange.com/a/293231/70524 – muru Sep 21 '23 at 23:44
  • @muru "No, it doesn't." Corrected. – Rubem Pacelli Sep 22 '23 at 03:44
  • @muru "if you mean using xargs is the solution". No. The usage of xargs, pipes, etc has been done millions years ago. I obviously not saying that the other questions don't use them. I am saying that my solution, xargs echo < my_file.txt, cannot be found in the other answers, let alone my explanations concerning redirection. – Rubem Pacelli Sep 22 '23 at 03:50
0

In bash, also the following should work:

echo `cat my_file.txt`

The part in backticks is replaced by the output of that command, i.e. in this case replaced by the file contents.

Motlib
  • 11
  • 1
    True, backticks work like $( . . . ). People who use $() usually prefer them because you can nest them. This question is old, though, it's much more satisfying to answer questions that are not already answered! – Law29 Jul 01 '16 at 09:12
  • Not quite. There is a subtle difference. When the back-tick form is used, a backslash retains its literal meaning except when followed by $, `, or . The first back-tick not preceded by a backslash terminates the command.

    When using the $(,,,) form, all characters between the parentheses make up the command.

    – fpmurphy Jul 01 '16 at 11:29
  • As that command substitution is not quoted, the split+glob operator would be applied to it. For instance if my_file.txt contains *, that would print the list of file names in the current directory. – Stéphane Chazelas May 08 '17 at 08:41