1 of 19

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 mote 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 (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 (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 (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 (2002):

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

AIX also has an istat command which will dump all the stat()/lstat() information and which you could post-process.

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.

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.