3 of 25
added details for AIX output; mote/note typo
Jeff Schaller
  • 67.3k
  • 35
  • 116
  • 255

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:

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 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.

Stéphane Chazelas
  • 545.8k
  • 92
  • 1.1k
  • 1.6k