Depends what you mean by _size_.

    size=$(wc -c < "$file")

will give you the number of bytes that can be read from the file. IOW, it's the size of the content of the file. It will however read the content of the file (except if the file is a regular file or symlink to regular file in most `wc` implementations as an optimisation). That may have side effects. For instance, for a named pipe, what has been read can no longer be read again.

That's standard and portable, however note that some `wc` implementations may include leading blanks in that output. One way to get rid of them is to use:

    size=$(($(wc -c < "$file")))

`ksh93` has `wc` builtin (provided  you enable it, you can also invoke it as `command /opt/ast/bin/wc`) which makes it the most efficient for regular files in that shell.

Various systems have a command called `stat` that's an interface to the `stat()` or `lstat()` system calls.

Those report information found in the inode. One of that information is the `st_size` attribute. For regular files, that's the size of the content (how much data could be read from it in the absence of error (that's what most `wc -c` implementations use in their optimisation)). For symlinks, that's the size in bytes of the target path. For named pipes, depending on the system, it's either 0 or the number of bytes currently in the pipe buffer...

By chronological order, there is:

- [IRIX `stat`](http://nixdoc.net/man-pages/IRIX/stat.1.html) (90's):

        stat -Ls -- "$file"

 returns the `st_size` attribute of `$file` (`lstat()`) or:

        stat -s -- "$file"

 same except when `$file` is a symlink in which case it's the `st_size` of the file after symlink resolution.

- [`zsh` `stat` builtin](http://zsh.sourceforge.net/Doc/Release/Zsh-Modules.html#The-zsh_002fstat-Module) (now also known as `zstat`) in the `zsh/stat` module (loaded with `zmodload zsh/stat`) (1997):

        stat -L +size -- $file # st_size of file
        stat +size -- $file    # after symlink resolution

 or to store in a variable:

        stat -L -A size +size -- $file

 obviously, that's the most efficient in that shell.

- [GNU `stat`](https://www.gnu.org/software/coreutils/manual/html_node/stat-invocation.html) (2001):

        stat -c %s -- "$file"  # st_size of file
        stat -Lc %s -- "$file" # after symlink resolution

 (note the meaning of `-L` is reversed compared to IRIX or `zsh` `stat`.

- [BSDs `stat`](https://www.freebsd.org/cgi/man.cgi?query=stat&sektion=1) (2002):

        stat -f %z -- "$file"  # st_size of file
        stat -Lf %z -- "$file" # after symlink resolution

AIX also has an [`istat` command](https://www.ibm.com/support/knowledgecenter/ssw_aix_72/com.ibm.aix.cmds3/istat.htm) which will dump all the `stat()`/`lstat()` information and which you could post-process with, for example:

    istat "$file" | awk '/Length/ { print $5 }'

Long before GNU introduced its `stat` command, the same could be achieved with GNU `find` command with its `-printf` predicate (already in 1991):

    find -- "$file" -prune -printf '%s\n' #    st_size of file
    find -L -- "$file" -prune -printf '%s\n' # after symlink resolution

One issue though is that doesn't work if `$file` starts with `-` or is a `find` predicate (like `!`, `(`...).

The standard command to get the `stat()`/`lstat()` information is `ls`.

POSIXly, you can do:

    LC_ALL=C ls -dn -- "$file" | awk '{print $5; exit}'

and add `-L` for the same after symlink resolution.

For block device, `stat()` returns 0 for `st_size`. Some systems have other APIs to report the size of the block device. For instance, Linux has the `BLKGETSIZE64` `ioctl()`, and most Linux distributions now ship with a `blockdev` command that can make use of it:

    blockdev --getsize64 -- "$device_file"

However, you need read permission to the device file for that. It's usually possible to derive the size by other means. For instance (still on Linux):

    lsblk -bdno size -- "$device_file"

Should work except for empty devices.

An approach that works for all _seekable_ files (so includes regular files, most block devices and some character devices) is to open the file and seek to the end:

- With `zsh` (after loading the `zsh/system` module):

        {sysseek -w end 0 && size=$((systell(0)))} < $file

- With `ksh93`:

        : < "$file" <#((size=EOF))

- with `perl`:

        perl -le 'seek STDIN, 0, 2 or die "seek: $!"; print tell STDIN' < "$file"

For named pipes, we've seen that some systems make the amount of data in the pipe buffer available in `stat()`'s `st_size`. That's rare though.

On Linux at least, you can use the `FIONREAD` `ioctl()`:

     perl -le 'require "sys/ioctl.ph";
               ioctl(STDIN, &FIONREAD, $n) or die$!;
               print unpack "L", $n' <> "$fifo_file"

However note that while it doesn't _read_ the content of the pipe, the mere opening of a named pipes can have side effects.