在shell中获取文件大小(以字节为单位)的可移植方式?


121

在Linux上,我使用stat --format="%s" FILE,但是我可以访问的Solaris没有stat命令。那我该怎么用呢?

我正在编写Bash脚本,不能真正在系统上安装任何新软件。

我已经考虑过使用:

perl -e '@x=stat(shift);print $x[7]' FILE

甚至:

ls -nl FILE | awk '{print $5}'

但是,这些看上去都不明智-运行Perl只是为了获取文件大小?还是运行2个命令来执行相同的操作?


1
bash脚本就是软件,如果可以将其放到系统上,则可以安装软件。
只是有人

4
从技术上讲-是的。我的意思是我没有root特权,并且无​​法安装新软件包。确保可以在主目录中安装。但是,当我不得不制作可移植的脚本并将其安装在“ X”计算机上时,并不是真的,新的附加软件包变得棘手。

Answers:


207

wc -c < filename(字数的缩写,-c打印字节数)是一种便携式POSIX解决方案。在某些平台上,仅输出格式可能不统一,因为可能会预先加上一些空格(对于Solaris就是这种情况)。

不要忽略输入重定向。当文件作为参数传递时,文件名将在字节数之后打印。

我担心它不适用于二进制文件,但是在Linux和Solaris上都可以正常工作。您可以尝试使用wc -c < /usr/bin/wc。此外,除非另有明确说明,否则POSIX实用程序可确保处理二进制文件


67
或者只是wc -c < file不想让文件名出现。
caf

34
但是,如果我没记错的话,wc在管道中必须read()整个流来计算字节数。该ls/ awk解决方案(以及类似)使用系统调用来获取大小,它应该是线性的时间(与O(大小))
jmtd

1
我记得wc上次我在完整的硬盘上这样做时非常慢。速度太慢了,我可以在第一个脚本完成之前重新编写脚本,来到这里来记住我是如何做到的。
卡米洛·马丁

6
我不会用wc -c; 它看起来更整洁,但是ls+ awk在速度/资源使用上更好。另外,我只是想指出您实际上还需要对结果进行后处理,wc因为在某些系统上,结果之前将有空白,在进行比较之前可能需要将其删除。
Haravikk

3
wc -c很棒,但是如果您没有对该文件的读取权限,它将无法正常工作。
西拉斯(Silas)

41

我最终编写了自己的程序(非常小)以仅显示大小。此处的更多信息:http : //fwhacking.blogspot.com/2011/03/bfsize-print-file-size-in-bytes-and.html

我认为常见的Linux工具最干净的两种方法是:

$ stat -c %s /usr/bin/stat
50000

$ wc -c < /usr/bin/wc
36912

但是我只是不想输入参数或通过管道输出来获取文件大小,所以我使用自己的bfsize。


2
问题描述的第一行指出,stat不是一个选项,而wc -c是一年多以来的最佳答案,所以我不确定这个答案的意义是什么。

22
关键在于像我这样的人,他们在Google中找到这个SO问题,stat 他们的选择。
2012年

3
我正在一个嵌入式系统上工作,该系统在wc -c10 MB的文件上需要4090毫秒的时间,而在“ 0”毫秒上需要10毫秒的时间stat -c %s,所以我同意,即使他们没有回答确切的问题,使用替代解决方案也是有帮助的。
罗伯特·卡尔洪

3
“ stat -c”不可移植/在MacOS上不接受与Linux上相同的参数。对于大文件,“ wc -c”将非常慢。
Orwellophile

2
stat也不是可移植的。stat -c %s /usr/bin/stat stat: illegal option -- c usage: stat [-FlLnqrsx] [-f format] [-t timefmt] [file ...]

27

即使du通常打印磁盘使用情况而不是实际数据大小,GNU coreutils du也可以以字节为单位打印文件的“表观大小”:

du -b FILE

但是它在BSD,Solaris,macOS,...下无法工作


3
在Mac OS X,brew install coreutils并且gdu -b会达到同样的效果
何塞·阿尔班

1
我更喜欢这种方法,因为wc需要立即读取整个文件才能得出结果du
CousinCocaine

2
POSIX du -bdu理论上完全不同的上下文中提及。
Palec

这仅使用lstat调用,因此其性能不取决于文件大小。比短stat -c '%s',但不那么直观,并且对文件夹的作用不同(内部每个文件的打印大小)。
Palec's

FreeBSDdu可以使用关闭du -A -B1,但是它仍然以1024B块的倍数打印结果。没有设法让它打印字节数。即使BLOCKSIZE=1在环境中设置也无济于事,因为那时使用了512B块。
Palec

13

最后我决定使用ls和bash数组扩展:

TEMP=( $( ls -ln FILE ) )
SIZE=${TEMP[4]}

它不是很好,但是至少它只执行1 fork + execve,并且不依赖于辅助编程语言(perl / ruby​​ / python / whatever)


放在一边-不需要'-ln'中的'l';'-n'与'-ln'完全相同
13年

不,这不对。只是比较输出。

1
有人猜测便携式设备ls -ln FILE | { read _ _ _ _ size _ && echo "$size"; }不需要使用第二步,因为它只使用内置功能,而是Linux上的Bash 4.2.37进行了两次分叉(execve尽管仍然只有一个)。
Palec

read _ _ _ _ size _ <<<"$(exec ls -ln /usr/bin/wc)" && echo "$size"与单fork和单exec一起使用,但它在此处字符串使用临时文件。通过使用兼容POSX的here-document替换here-string,可以使其变得可移植。顺便说一句,注意exec在子shell。否则,Bash会对子shell执行一个分支,而对内部运行的命令执行另一个分支。您在此答案中提供的代码就是这种情况。太。
Palec

1
-l是在存在多余-n。引用POSIX手册ls::-n打开-l(ell)选项,但是在写文件的所有者或组时,分别写文件的数字UID或GID而不是用户名或组名。禁用-C-m-x选项。
Palec

8

跨平台最快的解决方案(仅对ls使用single fork(),不尝试计算实际字符,不生成不需要的awk,perl等)。

在MacOS和Linux上进行了测试-可能需要对Solaris进行少量修改:

__ln=( $( ls -Lon "$1" ) )
__size=${__ln[3]}
echo "Size is: $__size bytes"

如果需要,简化ls参数,并调整$ {__ ln [3]}中的偏移量。

注意:将遵循符号链接。


1
或将其放在shell脚本中:ls -Lon“ $ 1” | awk'{print $ 4}'
Luciano

1
@Luciano我想你已经完全错过的点不分叉和做任务中的庆典,而不是使用bash字符串很多UNIX的低效方式命令组合在一起。
Orwellophile

8

BSD具有stat与GNU coreutils不同的选项,但功能相似。

stat -f %z <file name> 

这适用于macOS(在10.12 上测试),FreeBSDNetBSDOpenBSD


但是,Solaris根本没有stat实用程序。
Palec

6

在处理ls -n输出时,作为不适当地使用的shell数组的替代方法,可以使用位置参数,这些位置参数形成唯一的数组,并且是标准shell中唯一的局部变量。在函数中覆盖位置参数的覆盖,以将原始参数保留到脚本或函数中。

getsize() { set -- $(ls -dn "$1") && echo $5; }
getsize FILE

这将ln -dn根据当前IFS环境变量设置拆分输出,将其分配给位置参数并回显第五个参数。在-d确保目录得到妥善处理和-n用户名和组名不需要保证得到解决,不像-l。此外,理论上,包含空格的用户名和组名可能会破坏预期的行结构;通常不允许这样做,但是这种可能性仍然会使程序员停下来思考。


5

如果find从GNU fileutils 使用:

size=$( find . -maxdepth 1 -type f -name filename -printf '%s' )

不幸的是,其他实现方式find通常不支持-maxdepth,也不是-printf。例如,Solaris和macOS就是这种情况find


不需要FYI maxdepth。可以将其重写为size=$(test -f filename && find filename -printf '%s')
Palec 2014年

@Palec:-maxdepth旨在防止find递归(因为statOP不需要替换)。您的find命令缺少a,-name并且该test命令不是必需的。
暂停,直到另行通知。

@DennisWilliamson find递归搜索其参数以查找与给定条件匹配的文件。如果参数不是目录,则递归非常简单。因此,我首先测试这filename确实是一个现有的普通文件,然后使用find该文件打印其大小而无处可寻。
Palec 2014年

1
find . -maxdepth 1 -type f -name filename -printf '%s'仅当文件位于当前目录中时,该文件才有效,并且它仍可以检查目录中的每个文件,这可能会很慢。更好地使用(甚至更短!)find filename -maxdepth 1 -type f -printf '%s'
Palec

3

您可以使用find命令来获取一些文件集(此处提取了临时文件)。然后,您可以使用du命令使用-hswitch来以可读格式获取每个文件的文件大小。

find $HOME -type f -name "*~" -exec du -h {} \;

输出:

4.0K    /home/turing/Desktop/JavaExmp/TwoButtons.java~
4.0K    /home/turing/Desktop/JavaExmp/MyDrawPanel.java~
4.0K    /home/turing/Desktop/JavaExmp/Instream.java~
4.0K    /home/turing/Desktop/JavaExmp/RandomDemo.java~
4.0K    /home/turing/Desktop/JavaExmp/Buff.java~
4.0K    /home/turing/Desktop/JavaExmp/SimpleGui2.java~

2

您的第一个Perl示例对我来说似乎并不合理。

出于这种原因,我从编写shell脚本(使用bash / sh等)迁移到编写Perl中除了最琐碎的脚本之外的所有脚本。我发现我必须为特定要求启动Perl,并且随着我做的越来越多,我意识到用Perl编写脚本可能更强大(就语言和可通过CPAN获得的各种库而言))和更有效的方法来实现我想要的。

请注意,其他shell脚本语言(例如python / ruby​​)无疑具有类似的功能,您可能需要针对自己的目的对其进行评估。我只讨论Perl,因为这是我使用并熟悉的语言。


好吧,我做了很多Perl自己写的文章,但有时是为我选择了该工具,而不是我自己:)

-3

如果您的Solaris上有Perl,请使用它。否则,使用awk的ls是您的下一个最佳选择,因为您没有统计信息或者您的发现不是GNU查找。


-3

我使用的Solaris中有一个技巧,如果您要求多个文件的大小,则它只会返回不带名称的总大小-因此,请包含一个空文件(如/ dev / null)作为第二个文件:

例如命令fileyouwant / dev / null

我无法记住该命令适用于ls / wc / etc的哪个大小命令-不幸的是,我没有solaris盒来测试它。


-4

在Linux上,您可以使用du -h $FILE,这也适用于solaris吗?


1
实际上,单位可以转换,但是这显示的是磁盘使用情况,而不是文件数据大小(“表观大小”)。
Palec

-7

您是否尝试过du -ks | awk'{print $ 1 * 1024}'。那也许行得通。


1
这显示磁盘使用情况,而不是文件数据大小(“表观大小”)。
Palec
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.