在shell中,如何tail
在目录中创建最新文件?
在shell中,如何tail
在目录中创建最新文件?
Answers:
千万不能解析LS的输出!解析ls的输出是困难且不可靠的。
如果必须执行此操作,建议您使用find。最初,我在这里有一个简单的示例,只是为了向您提供解决方案的要点,但是由于此答案似乎很受欢迎,因此我决定对其进行修改,以提供可安全复制/粘贴和在所有输入中使用的版本。您坐得舒适吗?我们将从oneliner开始,它将为您提供当前目录中的最新文件:
tail -- "$(find . -maxdepth 1 -type f -printf '%T@.%p\0' | sort -znr -t. -k1,2 | while IFS= read -r -d '' -r record ; do printf '%s' "$record" | cut -d. -f3- ; break ; done)"
现在还不是一个班轮,是吗?在这里,它还是一个shell函数,并经过格式化以便于阅读:
latest-file-in-directory () {
find "${@:-.}" -maxdepth 1 -type f -printf '%T@.%p\0' | \
sort -znr -t. -k1,2 | \
while IFS= read -r -d '' -r record ; do
printf '%s' "$record" | cut -d. -f3-
break
done
}
现在,作为一个单行:
tail -- "$(latest-file-in-directory)"
如果其他所有方法都失败,则可以.bashrc
一并告诫您将上述功能包括在内,并考虑解决的问题。如果您只是想完成工作,则无需进一步阅读。
需要注意的是,以一个或多个换行符结尾的文件名仍不会tail
正确传递。解决此问题非常复杂,我认为如果遇到这样一个恶意文件名就足够了,而不会出现更危险的错误,而遇到“没有这样的文件”错误是相对安全的行为。
出于好奇,这是有关其工作方式,为何安全性以及为何其他方法可能无效的繁琐解释。
首先,可以安全地分隔文件路径的唯一字节为null,因为它是Unix系统上文件路径中普遍禁止的唯一字节。在处理任何文件路径列表时,仅将null用作定界符,甚至在将单个文件路径从一个程序传递到另一个程序时,以不会阻塞任意字节的方式进行操作,这一点很重要。有许多看似正确的方法可以解决此问题和其他问题,但这些方法都失败了,甚至(假设是偶然地)假设文件名中将没有换行或空格。两种假设都不安全。
对于今天的目的,第一步是找到一个以空分隔的文件列表。如果您有诸如GNU的find
支持-print0
,这很容易:
find . -print0
但是此列表仍然无法告诉我们哪个是最新的,因此我们需要包括该信息。我选择使用find的-printf
开关,它使我可以指定输出中将显示哪些数据。并非所有版本的find
支持-printf
(它都不是标准的),但是GNU find都支持。如果您发现自己不行,-printf
那么您将需要依靠-exec stat {} \;
这一点,您必须放弃所有便携性的希望,因为stat
这也不是标准的。现在,我将继续假设您拥有GNU工具。
find . -printf '%T@.%p\0'
在这里,我要的是printf格式%T@
,它是自Unix纪元开始以来的修改时间(以秒为单位),后跟一个句点,然后是一个表示秒的分数的数字。我添加了另一个时间段,然后添加了%p
(这是文件的完整路径),然后以空字节结尾。
我现在有
find . -maxdepth 1 \! -type d -printf '%T@.%p\0'
它可能不言而喻,但是为了完整起见,它会-maxdepth 1
阻止find
列出子目录的内容并\! -type d
跳过您不希望的目录tail
。到目前为止,我在当前目录中具有包含修改时间信息的文件,因此现在我需要按该修改时间进行排序。
默认情况下,sort
期望其输入为换行符分隔的记录。如果您有GNU sort
,则可以要求它使用空分隔记录,而不是使用-z
switch。对于标准sort
,没有解决方案。我只对前两个数字(秒和几分之一秒)排序感兴趣,不想按实际文件名排序,所以我告诉了sort
两件事:首先,它应该考虑句点(.
)作为字段定界符其次,在考虑如何对记录进行排序时,它仅应使用第一和第二字段。
| sort -znr -t. -k1,2
首先,我捆绑了三个毫无价值的短期选择。-znr
只是一种简洁的说法-z -n -r
)。之后-t .
(空格是可选的)告诉sort
字段定界符并-k 1,2
指定字段编号:第一和第二(sort
从一个字段开始计数,而不是从零开始)。请记住,当前目录的示例记录如下所示:
1000000000.0000000000../some-file-name
这意味着sort
将在订购此记录时先查看,1000000000
然后再查看0000000000
。该-n
选项告诉sort
您在比较这些值时使用数字比较,因为两个值都是数字。这可能并不重要,因为数字是固定长度的,但没有害处。
给予的另一个开关sort
是-r
“反向”。默认情况下,数字排序的输出将首先是最低的数字,然后对其进行-r
更改,以使其最后列出最低的数字,然后首先列出最高的数字。由于这些数字是时间戳记,因此越高意味着越新,这会将最新记录放在列表的开头。
随着文件路径列表的出现,sort
现在我们可以在顶部找到所需的答案。剩下的就是找到一种方法来丢弃其他记录并剥离时间戳。不幸的是,即使GNU head
和tail
不接受的开关,使他们对空分隔的输入操作。相反,我使用while循环作为一种穷人head
。
| while IFS= read -r -d '' record
首先,我将其设置IFS
为不对文件列表进行分词。接下来,我讲read
两件事:不要解释输入(-r
)中的转义序列,并且输入以空字节(-d
)分隔;此处的空字符串''
用于表示“无定界符”,又以null分隔。每个记录都将读入变量,record
以便每次while
循环迭代时,它都有一个时间戳和一个文件名。注意这-d
是一个GNU扩展;如果您只有一个标准,则read
此技术将无效,并且您的资源也很少。
我们知道record
变量有三个部分,所有部分都用句点字符分隔。使用该cut
实用程序可以提取其中的一部分。
printf '%s' "$record" | cut -d. -f3-
在这里,整个记录都通过printf
管道传递到那里cut
;在bash中,您可以使用here字符串进一步简化此操作,cut -d. -3f- <<<"$record"
以提高性能。我们讲cut
两件事:首先-d
,它应该有一个用于标识字段的特定定界符(就像使用sort
定界符一样.
)。其次cut
是指示-f
仅打印特定字段中的值;字段列表以范围的3-
形式给出,该范围指示来自第三字段和所有后续字段的值。这意味着cut
它将读取并忽略直到.
在记录中找到的所有内容,包括第二个内容,然后将打印其余部分,即文件路径部分。
打印了最新的文件路径后,无需继续进行:break
退出循环而无需继续前进到第二个文件路径。
唯一剩下的就是tail
在该管道返回的文件路径上运行。在我的示例中,您可能已经注意到,我是通过将管道封闭在子外壳中来完成此操作的;您可能没有注意到的是,我将子外壳括在双引号中。这一点很重要,因为即使为了确保所有文件名的安全而进行了所有这些努力,未加引号的subshell扩展仍然可能破坏事情。一个更详细的解释,如果你有兴趣,请。调用的第二个重要但容易忽略的方面tail
是,我--
在扩展文件名之前为其提供了选项。这将指示tail
这样就不会再指定任何选项,并且后面的所有内容都是文件名,这使得可以安全地处理以开头的文件名-
。
tail -f $(find . -type f -exec stat -f "%m {}" {} \;| sort -n | tail -n 1 | cut -d ' ' -f 2)
head
,tail
也不接受使它们对以空分隔的输入进行操作的开关。” 我的替代品head
:… | grep -zm <number> ""
。
tail `ls -t | head -1`
如果您担心文件名带有空格,
tail "`ls -t | head -1`"
您可以使用:
tail $(ls -1t | head -1)
该$()
构造启动一个子外壳,该子外壳运行命令ls -1t
(按时间顺序列出所有文件,每行一个),并通过管道进行传递head -1
以获取第一行(文件)。
该命令的输出(最新文件)然后传递tail
到进行处理。
请记住,如果这是最新创建的目录条目,则冒着获取目录的风险。我在别名中使用了该技巧,以在仅包含那些日志文件的目录中编辑最新日志文件(来自旋转集)。
-1
是没有必要的,ls
这是否给你当它是在管道。比较ls
和ls|cat
,例如。
在zsh
:
tail *(.om[1])
请参阅:http: //zsh.sourceforge.net/Doc/Release/Expansion.html#Glob-Qualifiers,这里m
表示修改时间m[Mwhms][-|+]n
,并且前面的o
意思是它以一种方式排序(以另一种方式O
排序)。的.
唯一手段常规文件。在方括号内[1]
选择第一个项目。挑三用[1,3]
,以获得最旧的用途[-1]
。
它很短,并且不使用ls
。
有人张贴了它,然后由于某种原因将其删除,但这是唯一可行的方法,所以...
tail -f `ls -tr | tail`
ls
不是最明智的事情……
tail -f `ls -lt | grep -v ^d | head -2 | tail -1 | tr -s " " | cut -f 8 -d " "`
说明: