在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个命令来执行相同的操作?
在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个命令来执行相同的操作?
Answers:
wc -c < filename
(字数的缩写,-c
打印字节数)是一种便携式POSIX解决方案。在某些平台上,仅输出格式可能不统一,因为可能会预先加上一些空格(对于Solaris就是这种情况)。
不要忽略输入重定向。当文件作为参数传递时,文件名将在字节数之后打印。
我担心它不适用于二进制文件,但是在Linux和Solaris上都可以正常工作。您可以尝试使用wc -c < /usr/bin/wc
。此外,除非另有明确说明,否则POSIX实用程序可确保处理二进制文件。
wc -c < file
不想让文件名出现。
wc
在管道中必须read()
整个流来计算字节数。该ls
/ awk
解决方案(以及类似)使用系统调用来获取大小,它应该是线性的时间(与O(大小))
wc
上次我在完整的硬盘上这样做时非常慢。速度太慢了,我可以在第一个脚本完成之前重新编写脚本,来到这里来记住我是如何做到的。
wc -c
; 它看起来更整洁,但是ls
+ awk
在速度/资源使用上更好。另外,我只是想指出您实际上还需要对结果进行后处理,wc
因为在某些系统上,结果之前将有空白,在进行比较之前可能需要将其删除。
wc -c
很棒,但是如果您没有对该文件的读取权限,它将无法正常工作。
我最终编写了自己的程序(非常小)以仅显示大小。此处的更多信息: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。
stat
是他们的选择。
wc -c
10 MB的文件上需要4090毫秒的时间,而在“ 0”毫秒上需要10毫秒的时间stat -c %s
,所以我同意,即使他们没有回答确切的问题,使用替代解决方案也是有帮助的。
即使du
通常打印磁盘使用情况而不是实际数据大小,GNU coreutils du
也可以以字节为单位打印文件的“表观大小”:
du -b FILE
但是它在BSD,Solaris,macOS,...下无法工作
brew install coreutils
并且gdu -b
会达到同样的效果
wc
需要立即读取整个文件才能得出结果du
。
lstat
调用,因此其性能不取决于文件大小。比短stat -c '%s'
,但不那么直观,并且对文件夹的作用不同(内部每个文件的打印大小)。
最后我决定使用ls和bash数组扩展:
TEMP=( $( ls -ln FILE ) )
SIZE=${TEMP[4]}
它不是很好,但是至少它只执行1 fork + execve,并且不依赖于辅助编程语言(perl / ruby / python / whatever)
ls -ln FILE | { read _ _ _ _ size _ && echo "$size"; }
不需要使用第二步,因为它只使用内置功能,而是Linux上的Bash 4.2.37进行了两次分叉(execve
尽管仍然只有一个)。
read _ _ _ _ size _ <<<"$(exec ls -ln /usr/bin/wc)" && echo "$size"
与单fork和单exec一起使用,但它在此处字符串使用临时文件。通过使用兼容POSX的here-document替换here-string,可以使其变得可移植。顺便说一句,注意exec
在子shell。否则,Bash会对子shell执行一个分支,而对内部运行的命令执行另一个分支。您在此答案中提供的代码就是这种情况。太。
跨平台最快的解决方案(仅对ls使用single fork(),不尝试计算实际字符,不生成不需要的awk,perl等)。
在MacOS和Linux上进行了测试-可能需要对Solaris进行少量修改:
__ln=( $( ls -Lon "$1" ) )
__size=${__ln[3]}
echo "Size is: $__size bytes"
如果需要,简化ls参数,并调整$ {__ ln [3]}中的偏移量。
注意:将遵循符号链接。
在处理ls -n
输出时,作为不适当地使用的shell数组的替代方法,可以使用位置参数,这些位置参数形成唯一的数组,并且是标准shell中唯一的局部变量。在函数中覆盖位置参数的覆盖,以将原始参数保留到脚本或函数中。
getsize() { set -- $(ls -dn "$1") && echo $5; }
getsize FILE
这将ln -dn
根据当前IFS
环境变量设置拆分输出,将其分配给位置参数并回显第五个参数。在-d
确保目录得到妥善处理和-n
用户名和组名不需要保证得到解决,不像-l
。此外,理论上,包含空格的用户名和组名可能会破坏预期的行结构;通常不允许这样做,但是这种可能性仍然会使程序员停下来思考。
如果find
从GNU fileutils 使用:
size=$( find . -maxdepth 1 -type f -name filename -printf '%s' )
不幸的是,其他实现方式find
通常不支持-maxdepth
,也不是-printf
。例如,Solaris和macOS就是这种情况find
。
size=$(test -f filename && find filename -printf '%s')
。
-maxdepth
旨在防止find
递归(因为stat
OP不需要替换)。您的find
命令缺少a,-name
并且该test
命令不是必需的。
find
递归搜索其参数以查找与给定条件匹配的文件。如果参数不是目录,则递归非常简单。因此,我首先测试这filename
确实是一个现有的普通文件,然后使用find
该文件打印其大小而无处可寻。
find . -maxdepth 1 -type f -name filename -printf '%s'
仅当文件位于当前目录中时,该文件才有效,并且它仍可以检查目录中的每个文件,这可能会很慢。更好地使用(甚至更短!)find filename -maxdepth 1 -type f -printf '%s'
。
您可以使用find
命令来获取一些文件集(此处提取了临时文件)。然后,您可以使用du
命令使用-h
switch来以可读格式获取每个文件的文件大小。
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~
您的第一个Perl示例对我来说似乎并不合理。
出于这种原因,我从编写shell脚本(使用bash / sh等)迁移到编写Perl中除了最琐碎的脚本之外的所有脚本。我发现我必须为特定要求启动Perl,并且随着我做的越来越多,我意识到用Perl编写脚本可能更强大(就语言和可通过CPAN获得的各种库而言))和更有效的方法来实现我想要的。
请注意,其他shell脚本语言(例如python / ruby)无疑具有类似的功能,您可能需要针对自己的目的对其进行评估。我只讨论Perl,因为这是我使用并熟悉的语言。