2011-05-04 40 views
75

在Bash中,我想创建一个函数,返回匹配特定模式的最新文件的文件名。例如,我有一个文件目录:Bash函数来寻找最新的文件匹配模式

Directory/ 
    a1.1_5_1 
    a1.2_1_4 
    b2.1_0 
    b2.2_3_4 
    b2.3_2_0 

我想要以'b2'开头的最新文件。我如何在bash中做到这一点?我需要在我的~/.bash_profile脚本中有这个。

+1

有关更多答案提示,请参阅http://superuser.com/questions/294161/unix-linux-find-and-sort-by-date-modified。排序是获取最新文件的关键步骤 – 2016-12-04 13:06:33

回答

139

ls命令有一个参数-t按时间排序。然后你可以用head -1抓住第一个(最新)。

ls -t b2* | head -1 

但要注意:Why you shouldn't parse the output of ls

我个人的看法是:解析ls只有危险的时候文件名可以包含一些奇怪的字符,如空格或换行符。如果你可以保证文件名不会包含有趣的字符,那么解析ls是相当安全的。

如果你正在开发一个脚本,许多人在很多系统上运行,许多不同的情况下,我非常推荐不要分析ls

这里是如何做到这一点“权利”:How can I find the latest (newest, earliest, oldest) file in a directory?

unset -v latest 
for file in "$dir"/*; do 
    [[ $file -nt $latest ]] && latest=$file 
done 
+5

对其他人的注意事项:如果您正在为目录执行此操作,则需要向ls添加-d选项,例如'ls -td |头-1' – 2012-12-27 19:53:27

+4

[解析LS](http://mywiki.wooledge.org/ParsingLs)链接表示不这样做,并建议在[BashFAQ 99](http://mywiki.wooledge.org/BashFAQ/099)。我正在寻找一个1-liner,而不是防弹包含在脚本中,所以我会继续像@lesmana那样不安分地解析ls。 – Eponymous 2014-06-05 00:07:37

+0

@同名:如果你正在寻找一个没有使用脆弱的'ls','printf'%s \ n“b2 * |头-1'将为你做。 – 2016-12-01 04:27:02

2

不寻常的文件名(如含有有效\n角色可以肆虐用这种分析的一个文件,这里有一个办法做到这一点的的Perl:

perl -le '@sorted = map {$_->[0]} 
        sort {$a->[1] <=> $b->[1]} 
        map {[$_, -M $_]} 
        @ARGV; 
      print $sorted[0] 
' b2* 

这是一个Schwartzian transform使用有

+0

可能schwartz与你同在! – 2016-06-24 17:45:57

+0

这个答案可能工作,但我不会信任它,因为糟糕的文档。 – 2016-12-04 12:56:27

+1

糟糕的文档?它带有一个wikipage ... – 2017-04-12 23:20:47

4

这是可能实现的。需要Bash功能:

# Print the newest file, if any, matching the given pattern 
# Example usage: 
# newest_matching_file 'b2*' 
# WARNING: Files whose names begin with a dot will not be checked 
function newest_matching_file 
{ 
    # Use ${1-} instead of $1 in case 'nounset' is set 
    local -r glob_pattern=${1-} 

    if (($# != 1)) ; then 
     echo 'usage: newest_matching_file GLOB_PATTERN' >&2 
     return 1 
    fi 

    # To avoid printing garbage if no files match the pattern, set 
    # 'nullglob' if necessary 
    local -i need_to_unset_nullglob=0 
    if [[ ":$BASHOPTS:" != *:nullglob:* ]] ; then 
     shopt -s nullglob 
     need_to_unset_nullglob=1 
    fi 

    newest_file= 
    for file in $glob_pattern ; do 
     [[ -z $newest_file || $file -nt $newest_file ]] \ 
      && newest_file=$file 
    done 

    # To avoid unexpected behaviour elsewhere, unset nullglob if it was 
    # set by this function 
    ((need_to_unset_nullglob)) && shopt -u nullglob 

    # Use printf instead of echo in case the file name begins with '-' 
    [[ -n $newest_file ]] && printf '%s\n' "$newest_file" 

    return 0 
} 

它只使用Bash内建函数,并且应该处理名称中包含换行符或其他不寻常字符的文件。

+0

你可以使用'nullglob_shopt = $(shopt -p nullglob)',然后''nullglob''把'nullglob'放回原来的状态。 – 2014-11-05 21:14:11

+0

@gniourf_gniourf使用$(shopt -p nullglob)的建议是一个很好的建议。我通常尽量避免使用命令替换('$()'或反引号),因为它很慢,特别是在Cygwin下,即使命令只使用内置函数。另外,运行命令的子shell上下文有时会导致它们以意想不到的方式运行。我也尝试避免在变量中存储命令(比如'nullglob_shopt'),因为如果你得到的变量值不对,会发生很糟糕的事情。 – pjh 2014-11-06 20:25:55

+0

我很欣赏对细节的关注,如果忽略了这些细节,可能会导致模糊的失败。谢谢! – 2016-07-31 18:32:07

2

有一个更有效的方法来实现这一点。请看下面的命令:

find . -cmin 1 -name "b2*" 

这个命令在与“B2 *”通配符搜索产生恰好一分钟前的最新文件。如果你想从最近两天的文件,那么你会更好使用以下命令:“”

find . -mtime 2 -name "b2*" 

的代表当前目录。 希望这有助于。

+5

这实际上并没有找到“最新的文件匹配模式”......它只是找到所有匹配模式的文件,这些文件是在一分钟前创建的,或者是两天前修改的。 – GnP 2017-09-12 03:23:15

+0

这个回答是基于提出的问题。此外,您可以调整该命令以查看大约一天前发布的最新文件。这取决于你想要做什么。 – Naufal 2017-09-12 15:35:46

2

findls行之有效的

  • 文件名不带换行的组合
  • 不是很大量文件的
  • 不是很长文件名

解决办法:

find . -name "my-pattern" ... -print | 
    xargs -0 ls -1 -t | 
    head -1 

让我们来分析一下:

随着find我们可以匹配所有类似的有趣的文件:

find . -name "my-pattern" ... 

然后使用-print0我们可以安全地通过所有的文件名的ls这样的:

find . -name "my-pattern" ... -print0 | xargs -0 ls -1 -t 

ls -t将文件排序通过修改时间(最新的第一个)并打印一行。您可以使用-c按创建时间进行排序。 注意:这将打破包含换行符的文件名。

终于head -1得到我们排序列表中的第一个文件。

注意:xargs使用系统限制参数列表的大小。如果这个尺寸超过,xargs将多次呼叫ls。这将打破排序,也可能是最终的输出。运行

xargs --show-limits 

检查系统的限制。