I want to replace cat:

var=$(cat "filename" 2>/dev/null)

by bashism syntax:


The problem is that I don't know how to make the bashism silent to avoid such warnings:

bash: filename: No such file or directory

I've tried this:

var=$(2>/dev/null <"filename")

but it does not read existing files into var anymore.


4 Answers 4


Wrapping the assignment into a compound block and using a redirection on that seems to work:

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


$ echo hello > test1; rm -f test2
$ file=test1; { var=$(<"$file"); } 2>/dev/null; echo "${var:-[it is empty]}"
$ file=test2; { var=$(<"$file"); } 2>/dev/null; echo "${var:-"[it is empty]"}"
[it is empty]

Just don't use ( .. ) to create a subshell, since then the assigned variable would be lost.

  • 1
    Thanks! I always forget that group command exists beside subshell.
    – rudimeier
    Commented Mar 6, 2018 at 13:56

Test first to make sure it's available and readable:

if [ -f "filename" ] && [ -r "filename" ]; then

The -f test tests whether filename refers to a regular file and the -r test tests whether it's readable by you.

  • I don’t think -f is really necessary... This still suffers from TOCTTOU but I doubt there’s anything better, short of using zsh (which handles 2> /dev/null here). Commented Mar 6, 2018 at 13:06
  • 1
    @StephenKitt Well, -r will still return true for a directory...
    – Kusalananda
    Commented Mar 6, 2018 at 13:10
  • 1
    Ah, yes, I can see that causing problems ;-). Perhaps ! -d would be better then; POSIX test -f excludes a few things it can be useful to redirect from (unlike BSD test -f which apparently means “exists and is not a directory”). Commented Mar 6, 2018 at 13:12
  • 2
    This would be a race condition, so that there might be still ugly output "sometimes". The files I read are maintained outside of my actual script. I just want to read and handle errors afterwards if any. But I really wonder that this extra "non-cat-bashism" seems to by less flexible than cat.
    – rudimeier
    Commented Mar 6, 2018 at 13:19

Another way is to first check if the file was exist then read it into variable.

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

For reference:

in zsh, you can use the $mapfile special associative array that maps to the content of the file named as the key (note that it's not related to and predates bash's mapfile builtin (which doesn't map files) by a decade or so). It uses mmap() so can only be used on regular files.

zmodload zsh/mapfile

It doesn't output anything if the file cannot be opened or mmaped. To check for errors, you can use the $ERRNO special variable:

if ((ERRNO)); then
  : the mapping failed

$ERRNO values can be translated to ENOENT, EACCESS... codes with the $errnos array in the zsh/system module, or to error strings with the syserror builtin in zsh/system as well.

You can also do mapfile[filename]=$var to change the content of the file, it is a true mapping.

Note that while $(<filename) would strip all trailing newline characters from the content of filename, $mapfile[filename] expands to the whole content of the file (including binary files) as-is.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .