如何在bash脚本中获取文件的大小?


Answers:


241

如果在GNU系统上,最好的选择是:

stat --printf="%s" file.any

来自man stat

%s总大小(以字节为单位)

在bash脚本中:

#!/bin/bash
FILENAME=/home/heiko/dummy/packages.txt
FILESIZE=$(stat -c%s "$FILENAME")
echo "Size of $FILENAME = $FILESIZE bytes."

注意:有关如何在Mac OS X的终端中使用stat的信息,请参阅@chbrown的答案


7
@ haunted85 stat是最简单的方法,假设您使用的是Linux或Cygwin(stat非标准)。wc -c正如Eugéne建议的那样,它是便携式的。
吉尔斯

2
stat: illegal option -- c
Iulian Onofrei

stat --printf="%s" file.txt不会在Debian Jessie上输出任何东西...
woohoo

5
在MacOS上,此方法有效:stat -f%z myfile.tar
ccpizza

2
@woohoo您的提示将覆盖输出。man stat说--printf省略了结尾的换行符。使用--format-c查看输出。stat --printf="%s" file.any | xxd -stat -c "%s" file.any | xxd -
孙子

92
file_size_kb=`du -k "$filename" | cut -f1`

使用的问题stat是它是GNU(Linux)扩展。 du -k并且cut -f1由POSIX指定,因此可移植到任何Unix系统。

例如,Solaris附带bash但不附带stat。因此,这并非完全是假设。

ls具有类似的问题,因为未指定输出的确切格式,因此无法移植地解析其输出。 du -h也是GNU扩展。

尽可能使用可移植的结构,将来您的生活会变得更轻松。也许是你自己的。


48
du没有给出文件的大小,而是指示文件使用了多少空间,这有细微的差别(通常,报告du的大小是文件的大小四舍五入到最接近的块数,其中一个块通常为512B或1kB或4kB)。
Gilles

7
@Gilles,稀疏文件(即文件中带有孔的文件)报告的长度小于长度。
vonbrand '16

5
--bytes-b代替-k,这应该是公认的答案。
Amedee Van Gasse

1
在一般情况下,的-h(“人类”)选项du将产生最合适的答案:file_size=`du -h "$filename" | cut -f1,因为它将在适当情况下显示K(千字节),M(兆字节)或G(千兆字节)。
fralau

1
@fralau:OP希望“将其分配给bash变量,以便他们以后可以使用它”,因此,他们更有可能想要一个实际的数值,而不是人类可读的近似值。另外,-h是GNU扩展;这不是标准
Nemo

72

您还可以使用“字数统计”命令(wc):

wc -c "$filename" | awk '{print $1}'

问题wc在于它将添加文件名并缩进输出。例如:

$ wc -c somefile.txt
    1160 somefile.txt

如果您想避免链接完整的解释语言或流编辑器只是为了获得文件大小计数,只需重定向文件中的输入,以使它wc永远不会看到文件名:

wc -c < "$filename"

最后一种形式可以与命令替换一起使用,以轻松地将您正在寻找的值用作shell变量,如下面的Gilles所述。

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

30
wc -c <"$FILENAME"给出的大小没有其他内容,因此size=$(wc -c <"$FILENAME")
吉尔斯

6
还有一点:我刚刚进行了测试,并且wc -c < file似乎非常快,至少在OS X上是如此。我猜测如果仅指定-c,wc​​就有能力尝试统计文件。
爱德华·福克

4
@EdwardFalk:GNU wc -c使用fstat,但随后查找文件的倒数第二个块并读取最后一个st_blksize字节。显然,这是因为,例如,Linux /proc和中的文件的/sys统计信息大小仅是近似值,并且wc想要报告实际大小,而不是统计信息报告的大小。我想wc -c报告一个不同于的大小会很奇怪wc,但是如果它是普通磁盘文件并且不在内存中,则从文件中读取数据并不理想。或更糟糕的是,近线磁带存储...
Peter Cordes

1
似乎printf仍然看到缩进,例如printf "Size: $size"-> size: <4 spaces> 54339。另一方面,echo忽略空白。有什么办法可以使其一致吗?
尤金·库拉布霍夫

2
@keithpjolley:通过致电fstat。尝试运行strace wc -c </etc/passwd,您会看到它在做什么。
Nemo

48

BSD(Mac OS X)stat具有不同的格式参数标志和不同的字段说明符。来自man stat(1)

  • -f format:使用指定的格式显示信息。有关有效格式的说明,请参见“格式”部分。
  • ...格式部分...
  • z:文件大小,以字节为单位。

所以现在在一起:

stat -f%z myfile1.txt

28

取决于您所说的尺寸

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

将为您提供可以从文件读取的字节数。IOW,它是文件内容的大小。但是,它将读取文件的内容(除非该文件是常规文件或在大多数wc实现中作为常规链接到常规文件的符号链接)。那可能会有副作用。例如,对于命名管道,已读取的内容将无法再读取,对于诸如/dev/zero/dev/random无限大的内容,则需要一段时间。这也意味着您需要read对该文件的许可,并且该文件的最后访问时间戳可能会更新。

这是标准且可移植的,但是请注意,某些wc实现可能在该输出中包含前导空格。摆脱它们的一种方法是使用:

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

或为避免在dashyash时不wc产生任何输出(例如无法打开文件的情况)时出现空算术表达式的错误:

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

ksh93具有wc内置功能(前提是您启用了该功能,也可以将其调用为command /opt/ast/bin/wc),这使得该Shell中的常规文件效率最高。

各种系统都有一个称为的命令stat,该命令是stat()lstat()系统调用的接口。

这些报告信息在inode中找到。该信息之一是st_size属性。对于常规文件,这就是内容的大小(在没有错误的情况下可以从该文件中读取多少数据(这是大多数wc -c实现优化使用的内容))。对于符号链接,这是目标路径的大小(以字节为单位)。对于命名管道,取决于系统,它可以是0或管道缓冲区中当前的字节数。与块设备相同,在块设备中,取决于系统,您将获得0或基础存储的字节大小。

您不需要对该文件的读取权限就可以获取该信息,而只需搜索对其链接到的目录的权限。

按照时间顺序,有:

  • IRIXstat(90年代):

    stat -qLs -- "$file"

    返回()的st_size属性,或:$filelstat()

    stat -s -- "$file"

    相同,除了何时$file是符号链接,在这种情况下,它是st_size符号链接解析后的文件的。

  • zsh stat内置zstatzsh/stat模块中(现在也称为)(已加载zmodload zsh/stat)(1997年):

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

    或存储在变量中:

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

    显然,那是该Shell中最有效的。

  • GNUstat(2001);stat自2005年起也在BusyBox中使用(从GNU复制stat):

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

    (请注意,-L与IRIX或相比,的含义相反zsh stat

  • BSDstat(2002):

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

或者,您可以使用某些脚本语言的stat()/ lstat()函数,例如perl

perl -le 'print((lstat shift)[7])' -- "$file"

AIX还具有一个istat命令,该命令将转储所有stat()(不是lstat(),因此不会在符号链接上使用)信息,并且可以使用以下命令进行后处理:

LC_ALL=C istat "$file" | awk 'NR == 4 {print $5}'

(感谢@JeffSchaller提供了详细帮助)。

tcsh

@ size = -Z $file:q

(符号链接解析后的大小)

在GNU引入其stat命令之前很久,使用GNU find命令及其-printf谓词就可以实现相同的功能(早在1991年):

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

但是,有一个问题是,如果$file-find谓词开头(如!(...),则不起作用。

获取stat()/ lstat()信息的标准命令是ls

POSIXly,您可以执行以下操作:

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

-L在符号链接解析后添加相同的内容。尽管第5 字段是设备主编号而不是大小,但这不适用于设备文件。

对于块设备,对于,stat()返回0的系统st_size通常具有其他API来报告块设备的大小。例如,Linux具有BLKGETSIZE64 ioctl(),并且大多数Linux发行版现在都附带blockdev可以使用它的命令:

blockdev --getsize64 -- "$device_file"

但是,您需要对此设备文件具有读取权限。通常可以通过其他方式得出尺寸。例如(仍然在Linux上):

lsblk -bdno size -- "$device_file"

应该可以正常工作,但空设备除外。

适用于所有可搜索文件(因此包括常规文件,大多数块设备和某些字符设备)的一种方法是打开文件并搜索到末尾:

  • 使用zsh(加载zsh/system模块后):

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

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

    要么

    { size=$(<#((EOF))); } < "$file"
  • perl

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

对于命名管道,我们已经看到,有些系统(AIX,Solaris和HP / UX至少)使在现有的管道缓冲区的数据量stat()st_size。有些(如Linux或FreeBSD)则没有。

至少在Linux上,FIONREAD ioctl()打开管道后可以使用after(在读写模式下避免挂起):

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

但是请注意,尽管它不读取管道的内容,但仅在此处打开命名管道仍会产生副作用。我们fuser用来首先检查某些进程是否已经打开管道以缓解这种情况,但这并不是万无一失的,因为fuser可能无法检查所有进程。

现在,到目前为止,我们仅考虑与文件关联的主要数据的大小。这没有考虑元数据的大小以及存储该文件所需的所有支持基础结构。

返回的另一个inode属性stat()st_blocks。那是用于存储文件数据(有时是一些元数据,例如Linux上ext4文件系统上的扩展属性)的512字节块的数量。这不包括索引节点本身,也不包括文件链接到的目录中的条目。

大小和磁盘使用情况不一定紧密相关,因为压缩,稀疏(有时是一些元数据),某些文件系统中的间接块之类的额外基础结构会对后者产生影响。

这通常就是du报告磁盘使用情况的方法。上面列出的大多数命令都可以为您提供该信息。

  • POSIXLY_CORRECT=1 ls -sd -- "$file" | awk '{print $1; exit}'
  • POSIXLY_CORRECT=1 du -s -- "$file" (不适用于其中包含文件的磁盘使用情况的目录)。
  • GNU find -- "$file" -printf '%b\n'
  • zstat -L +block -- $file
  • GNU stat -c %b -- "$file"
  • BSD stat -f %b -- "$file"
  • perl -le 'print((lstat shift)[12])' -- "$file"

显然是最全面,最有用的答案。谢谢。我可以使用它来使用BSD和GNU统计信息创建跨平台的bash脚本
oligofren

1
有趣的事实:GNU coreutils wc -c使用fstat,但随后读取最后一个st_blksize字节。显然,这是因为在Linux中的文件/proc/sys例如具有统计的尺寸是只是近似。这对于正确性有好处,但如果文件的末尾位于磁盘而不是内存中,则不好(特别是如果在循环中用于许多文件)。如果文件迁移到近线磁带存储或FUSE透明解压缩文件系统,则非常糟糕。
彼得·科德斯

也不会做这项工作ls -go file | awk '{print $3}'
Steven Penny

@StevenPenny这些-go将是SysV,它们将无法在BSD(POSIX中的可选(XSI))上运行。您还需要ls -god file | awk '{print $3; exit}'-d它可以在目录上工作,exit在目标中带有换行符的符号链接)。设备文件的问题也仍然存在。
斯特凡·查泽拉斯

1
@αғsнιηUnix API在文本文件和二进制文件之间没有区别。全部都是字节序列。某些应用程序可能希望将这些字节解释为文本,但显然不希望wc -c报告字节数。
–StéphaneChazelas

22

该脚本结合了多种计算文件大小的方法:

(
  du --apparent-size --block-size=1 "$file" 2>/dev/null ||
  gdu --apparent-size --block-size=1 "$file" 2>/dev/null ||
  find "$file" -printf "%s" 2>/dev/null ||
  gfind "$file" -printf "%s" 2>/dev/null ||
  stat --printf="%s" "$file" 2>/dev/null ||
  stat -f%z "$file" 2>/dev/null ||
  wc -c <"$file" 2>/dev/null
) | awk '{print $1}'

该脚本可在许多Unix系统上运行,包括Linux,BSD,OSX,Solaris,SunOS等。

文件大小显示字节数。它是表观大小,即文件在典型磁盘上使用的字节,没有特殊压缩,特殊稀疏区域或未分配的块等。

该脚本的生产版本在此处提供更多帮助和更多选项:https : //github.com/SixArm/file-size


9

stat似乎使用最少的系统调用来执行此操作:

$ set debian-live-8.2.0-amd64-xfce-desktop.iso

$ strace stat --format %s $1 | wc
    282    2795   27364

$ strace wc --bytes $1 | wc
    307    3063   29091

$ strace du --bytes $1 | wc
    437    4376   41955

$ strace find $1 -printf %s | wc
    604    6061   64793

8

ls -l filename 将为您提供有关文件的大量信息,包括文件大小,权限和所有者。

文件大小在第五列,以字节为单位显示。在下面的示例中,文件大小不到2KB:

-rw-r--r-- 1 user owner 1985 2011-07-12 16:48 index.php

编辑:这显然不如stat命令可靠。


我认为ls -lstat命令都提供可靠的尺寸信息。我没有发现任何相反的说法。ls -s将给出块的大小。
dabest1年

2
@ dabest1从某种意义上说在另一个Unix中,它们的输出可能不同(在某些Unix中是不同的),这是不可靠的。
Eugene Bujak

是的,IIRC,Solaris默认情况下不显示组名称,从而导致输出中的列减少。
爱德华·福尔克

由于大小为纯数字,由空格包围,而日期年份为纯数字,因此格式已定义,因此有可能使用正则表达式将用户+所有者视为一个字段,而无论该组是否存在。(适合读者的练习!)
MikeW

5

du filename 会以字节为单位告诉您磁盘使用情况。

我更喜欢du -h filename,它以可读的格式为您提供尺寸。


2
那或stat -c "%s";)

1
这种du打印输出的大小以1024字节为单位,而不是简单的字节计数。
彼得·里昂斯

请注意,standard du以512字节为单位给出输出。du除非POSIXLY_CORRECT在其环境中使用,否则GNU会使用kibibytes 。
斯特凡Chazelas

1
对于directory类型的文件,它提供目录的磁盘使用率,也提供其中的所有其他文件的磁盘使用率(递归)。
斯特凡Chazelas

3

在可以委托给您的Shell脚本中创建小的实用程序函数。

#! /bin/sh -
# vim: set ft=sh

# size utility that works on GNU and BSD systems
size(){
    case $(uname) in
        (Darwin | *BSD*)
            stat -Lf %z -- "$1";;
        (*) stat -c %s -- "$1"
    esac
}

for f do
    printf '%s\n' "$f : $(gzip < "$f" | wc -c) bytes (versus $(size "$f") bytes)"
done

基于@StéphaneChazelas的回答中的信息。


另请参阅gzip -v < file > /dev/null检查文件的可压缩性。
斯特凡Chazelas

@StéphaneChazelas不知道我是否认为这是一种改进。这些案例陈述很容易使菜鸟们望而却步;我当然不记得如何正确处理它们:-)自从您这样做以来,case语句在本质上更可移植吗?我认为有两种以上的情况很重要,但否则... +
oligofren

1
我想这也是一个品味问题,但这是您要使用case语句的典型情况。case是用于模式匹配的Bourne / POSIX构造。[[...]]仅限于ksh / bash / zsh(带有变体)。
斯特凡Chazelas

2

我找到了AWK 1班轮,它有一个错误,但我已将其修复。我还在TeraBytes之后添加了PetaBytes。

FILE_SIZE=234234 # FILESIZE IN BYTES
FILE_SIZE=$(echo "${FILE_SIZE}" | awk '{ split( "B KB MB GB TB PB" , v ); s=1; while( $1>1024 ){ $1/=1024; s++ } printf "%.2f %s", $1, v[s] }')

考虑到统计信息并非在每个系统上都存在,因此几乎可以始终使用AWK解决方案。例; Raspberry Pi没有统计信息,但确实有awk


1
完全不是OP的要求,而是很好的一小部分工作。
Gypsy Spellweaver

0

另一种与POSIX兼容的方式将awk与它的length()函数一起使用,该函数以输入文件每一行中的字符(不包括换行符)返回长度。所以这样做

awk '{ sum+=length } END { print sum+NR }' file

我们确保NR将添加到中sum,从而得出文件中遇到的字符总数和换行符总数。length()in中的函数awk采用一个参数,默认情况下表示length($0)该参数适用于当前整行。


若最后一行没有一个回车结束:printf 'a\nb' | awk '{ sum+=length } END { print sum+NR }'应该打印3打印,但4
艾萨克

-1

我自己喜欢wc选项。与“ bc”配对,您可以将小数位数尽可能多地取至任意位置。

我一直在寻找一个改善脚本的方法,该脚本已经'ls -alh'命令的'file size'列了。我不只是想要整数文件大小,而且两个小数点似乎也适合,因此在阅读了此讨论之后,我想到了下面的代码。

如果您在脚本中包含分号,建议不要在分号处加分。

file=$1; string=$(wc -c $file); bite=${string% *}; okay=$(echo "scale=2; $bite/1024" | bc);friend=$(echo -e "$file $okay" "kb"); echo -e "$friend"

我的脚本称为gpfl,用于“获取图片文件长度”。我用它做了之后mogrify在ImageMagick的文件,在GUI JPEG浏览器打开或重新加载图片前。

我不知道这是什么“答案”,因为它借鉴了已经提供和讨论的内容。所以我将其留在那里。

百事通


1
我更喜欢使用“ stat”或“ ls”。通常,我不喜欢使用“ wc”来获取文件大小,因为它会物理读取整个文件。如果您有很多文件,或者特别大的文件,则可能要花费很多时间。但是您的解决方案很有创意... + 1。
凯文·费根

2
我同意在文件大小上使用“ stat”而不是“ wc”的观点,但是,如果使用“ wc -c”,则不会读取任何数据;而是使用lseek来计算文件中的字节数。lingrok.org/xref/coreutils/src/wc.c#228
bbaja42 2014年

1
@ bbaja42:请注意,wc如果stat.st_size只是一个近似值(例如对于Linux /proc/sys文件),GNU Coreutils 会读取文件的最后一块。我猜想他们在添加一些逻辑后,决定不使主要意见变得更加复杂:lingrok.org/xref/coreutils/src/wc.c#246
Peter Cordes

-1

最快最简单的(IMO)方法是:

bash_var=$(stat -c %s /path/to/filename)

2
然后对提及stat的一个或多个现有答案进行投票;无需再次重复...
Jeff Schaller

1
@JeffSchaller我刚刚根据您的指示对Stephane的回答表示支持。我认为这对我来说太复杂了。这就是为什么我将这个简单的答案发布给志趣相投的人的原因。
WinEunuuchs2Unix

1
谢谢; 只是“统计”答案的第六个实例并不能简化此问答,而是希望新读者问自己“此答案与其他答案有何不同?” 并导致更多的混乱而不是更少。
杰夫·谢勒

我猜@JeffSchaller。但是我可能会抱怨很多duwc答案都应该有免责声明,在现实生活中绝对要这样做。今晚,我只是在现实生活中使用了我的答案,并认为值得分享。我想我们所有人都耸耸肩
WinEunuuchs2Unix
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.