10

I want to read text file contents into a Bash variable, suppressing the error message if that file does not exist. In POSIX, I would do

var=$(cat file 2>/dev/null)

But I read (e.g. at How to read a file into a variable in shell) that it's a Useless Use of Cat in Bash. So, I'm trying those:

var=$(< file 2>/dev/null)
var=$(< file) 2>/dev/null

But it the first doesn't read an existing file, and both print -bash: file: No such file or directory if the file does not exist. Why doesn't this work? (Especially: What completely breaks the first one?)

What does work is this:

{ var=$(< file); } 2>/dev/null

But it's ugly and cumbersome. So, is there a nicer syntax, or is this a valid use of cat after all?

Ingo Karkat
  • 167,457
  • 16
  • 250
  • 324

4 Answers4

7

With BASH it is easy to test if file exists before reading it in

[ -e file ] && var=$(< file )

As Inian points in the comments the file might exist but the user might not have sufficient rights for reading it. The -r test would take care of that.

[ -r file ] && var=$(< file )
Dima Chubarov
  • 16,199
  • 6
  • 40
  • 76
  • 1
    Does this handle the error stream when the file exists and permission is denied for the file? – Inian Aug 08 '17 at 12:34
  • True, but isn't this even worse than my alternatives: 1. Additional check and code branch (and potential race condition if the file vanishes between the two) 2. Issues with `-e` vs. `-r` (as @Inian also commented on) 3. Need to initialize `var=` in addition 4. Probably (slightly) worse performance – Ingo Karkat Aug 08 '17 at 12:36
  • 2
    Poor performance is baked into the shell :) Don't worry about unavoidable and negligible things like this. – chepner Aug 08 '17 at 12:42
  • @chepner: Sure, that's why I put it last in my list (just for completeness :-) – Ingo Karkat Aug 08 '17 at 12:52
4
var=$(cat file 2>/dev/null)

This is a Useful Use of Cat. It enforces an order of operations: stderr is redirected before file is opened. It serves a purpose. Don't feel bad about using it.

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
3

Redirection applies to the output of a command line. When the cat command produces an error, you can redirect the stderr as you've done.

But when you generate the error within the command line itself, through input redirection from a file that doesn't exist, THAT shell is the one producing the error, and you're not redirecting its output to anywhere special. You've successfully discovered the "ugly and cumbersome" workaround, which mimics a subshell using curly braces.

Personally, I'd use an if construct, but if you really prefer shorter code over fewer calls to external programs, this is a perfectly valid use of cat.

ghoti
  • 45,319
  • 8
  • 65
  • 104
  • 1
    Thanks. I chose your answer (even though it has received fewer votes) because it not just provides an opinion, but also explains the behavior I'd seen. – Ingo Karkat Aug 09 '17 at 07:20
0

Define a commands block with curly braces and redirect the stderr to /dev/null for the whole block.

{ IFS= read -rd '' var <file;} 2>/dev/null
Léa Gris
  • 17,497
  • 4
  • 32
  • 41