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脚本。

158816 次浏览

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

ls -t b2* | head -1

但是要小心: 为什么不解析 ls 的输出

我个人的观点是: 当文件名可能包含空格或换行等有趣的字符时,解析 ls是危险的。

如果您可以保证文件名不包含有趣的字符(可能是因为您控制了文件的生成方式) ,那么解析 ls是相当安全的。

如果您正在开发一个脚本,这个脚本应该由许多人在许多不同的情况下在许多系统上运行,那么不要解析 ls

以下是如何做到安全: 如何在目录中找到最新的(最新的、最早的、最老的)文件?

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

不常见的文件名(例如包含有效 \n字符的文件可能会对这种解析造成严重破坏。下面是在 Perl 中实现这一点的一种方法:

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

那里用的是 施瓦兹变换

这是所需 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 内置函数,并且应该处理名称包含换行符或其他异常字符的文件。

有一个更加有效的方法来实现这一点。考虑以下命令:

find . -cmin 1 -name "b2*"

此命令查找正好在一分钟前使用通配符搜索“ b2 *”生成的最新文件。如果你想要最近两天的文件,你最好使用下面的命令:

find . -mtime 2 -name "b2*"

“ .”代表工作目录。 希望这个能帮上忙。

findls的组合适合于

  • 没有换行的文件名
  • 不是很大的文件量
  • 不是很长的文件名

解决办法:

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

我们来分析一下:

使用 find,我们可以匹配所有这样有趣的文件:

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

然后使用 -print0,我们可以像下面这样安全地将所有文件名传递给 ls:

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

可以在这里添加其他 find搜索参数和模式

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

ls -t将按照修改时间(最新的第一次)对文件进行排序,并一行一行地打印。可以使用 -c按创建时间进行排序。注意: 这将与包含换行符的文件名中断。

最后,head -1为我们提供了排序列表中的第一个文件。

注意: xargs对参数列表的大小使用系统限制。如果这个大小超过,xargs将多次调用 ls。这将中断排序,可能还会中断最终输出。快跑

xargs  --show-limits

检查你们系统的极限。

注意2: 如果不想通过子文件夹搜索文件,请使用 find . -maxdepth 1 -name "my-pattern" -print0

注3: 正如@starfried--r参数所指出的,如果 find没有匹配任何文件,那么 xargs-r参数将阻止对 ls -1 -t的调用。谢谢你的建议。

你可以使用 stat和一个文件投球,一个装饰-排序-取消装饰,在前面添加文件时间:

$ stat -f "%m%t%N" b2* | sort -rn | head -1 | cut -f2-

如注释中所述,最好的跨平台解决方案可能是使用 Python、 Perl 和 Ruby 脚本。

对于这种情况,我倾向于使用 Ruby,因为它非常适合于编写小型的、可以随意丢弃的脚本,而且它在命令行中就具有 Python 或 Perl 的功能。

这是一颗红宝石:

ruby -e '
# index [0] for oldest and [-1] for newest
newest=Dir.glob("*").
reject { |f| File.directory?(f)}.
sort_by { |f| File.birthtime(f) rescue File.mtime(f)
}[-1]
p newest'

获取当前工作目录中的最新文件。

也可以通过在 glob中使用 **/*或者限制使用与 b2*匹配的文件等方法来实现全局递归

一个 Bash 函数,用于查找符合模式的目录下的最新文件

#1.  Make a bash function:
newest_file_matching_pattern(){
find $1 -name "$2" -print0 | xargs -0 ls -1 -t | head -1
}
 

#2. Setup a scratch testing directory:
mkdir /tmp/files_to_move;
cd /tmp/files_to_move;
touch file1.txt;
touch file2.txt;
touch foobar.txt;
 

#3. invoke the function:
result=$(newest_file_matching_pattern /tmp/files_to_move "file*")
printf "result: $result\n"

印刷品:

result: /tmp/files_to_move/file2.txt

或者,如果 brittle bash parlor 的把戏分包给 python 解释器更符合你的角度,那么它也会起到同样的作用:

#!/bin/bash
 

function newest_file_matching_pattern {
python - <<END
import glob, os, re
print(sorted(glob.glob("/tmp/files_to_move/file*"), key=os.path.getmtime)[0]);
END
}
 

result=$(newest_file_matching_pattern)
printf "result: $result\n"

印刷品:

result: /tmp/files_to_move/file2.txt

使用 find 命令。

假设您使用的是 Bash 4.2 + ,那么对文件时间戳值使用 -printf '%T+ %p\n'

find $DIR -type f -printf '%T+ %p\n' | sort -r | head -n 1 | cut -d' ' -f2

例如:

find ~/Downloads -type f -printf '%T+ %p\n' | sort -r | head -n 1 | cut -d' ' -f2

有关更有用的脚本,请参见这里的 最新发现脚本: https://github.com/l3x/helpers

对谷歌员工而言:

ls -t | head -1

  • -t按上次修改日期时间进行排序
  • head -1只返回第一个结果

(不要在生产中使用)