一行 bash 命令用于查找目录中的 N 个最旧和最新的文件



我有以下命令:

files=$(ls -lhrt dirname) && echo $files | head -5 && echo $files | tail -5

这个想法是返回目录中最旧和最新的 5 个文件dirname。这将返回请求的数据 - 但是,这些行混杂在一起。

有没有办法更好地格式化输出?(或者也许是编写此功能的更好方法)?

始终引用变量扩展以防止单词拆分和通解。当您离开$files时,不带引号的 bash 的单词拆分传递会导致换行符丢失。

files=$(ls -lhrt dirname) && echo "$files" | head -5 && echo "$files" | tail -5

使用&&运算符没有真正的好处。我只想写:

files=$(ls -lhrt dirname)
echo "$files" | head -5
echo "$files" | tail -5

或者,更好的是,将echo换成<<<以避免不必要的子流程。

files=$(ls -lhrt dirname)
head -5 <<< "$files"
tail -5 <<< "$files"
一起

头尾

巴(不必将先前命令的全部输出存储到一个变量中)

注意:按照这个,我将使用前 4 行和最后 4 行作为使用seq 1 100..的样本,但前 5 行和最后 5 行用于使用ls -lhrt dirname的样本。

第一种方式,连续使用headtail

如果您尝试:

seq 1 100000 | (head -n 4;tail -n 4;)
1
2
3
4
99997
99998
99999
100000

似乎可以完成这项工作,但是

seq 1 1000 | (head -n 4;tail -n 4;)
1
2
3
4

给出错误的答案。

这是由于缓冲,但 bash 允许您使用无缓冲输入:

seq 1 12 | { for i in {1..4};do read foo;echo "$foo";done;tail -n 4 ;}
1
2
3
4
9
10
11
12

最后

对于您的请求,请尝试以下操作:

{ for i in {1..5};do read foo;echo "$foo";done;tail -n 5;} < <(ls -lhrt dirname) 

必须符合您的需求。

或者在tee的帮助下同时使用两者

看看:

seq 1 12 | tee > >(tail -n4) >(head -n4)
1
2
3
4
9
10
11
12

但是这可能会在终端上呈现奇怪的事情,为了防止这种情况,您可以将整个管道传输到cat

seq 1 12 | tee > >(tail -n4) >(head -n4) | cat
1
2
3
4
9
10
11
12

所以

ls -lhrt dirname | tee > >(tail -n5) >(head -n5) | cat

必须完成这项工作。

甚至,如果你想玩bash和一个大变量:

files=$(seq 1 12) out='' in=''
for i in {1..4};do
in+=${files%%$'n'*}$'n'
files=${files#*$'n'}
out=${files##*$'n'}$'n'${out}
files=${files%$'n'*}
done
echo "$in${out%$'n'}"
1
2
3
4
9
10
11
12

另一方面:

files=$(ls -lhrt dirname) out='' in=''
for i in {1..5};do
in+=${files%%$'n'*}$'n'
files=${files#*$'n'}
out=${files##*$'n'}$'n'${out}
files=${files%$'n'*}
done
echo "$in${out%$'n'}"

但是你可以使用 GNU sed

seq 1 100000 | sed -e ':a;N;4p;5,${s/^[^n]*n//;};$!ba;'
1
2
3
4
99997
99998
99999
100000

然后

ls -lhrt dirname | sed -e ':a;N;5p;6,${s/^[^n]*n//;};$!ba;'

像这样添加换行符怎么样:

files=$(ls -lhrt dirname) && echo -e "${files}n" | head -5 && echo -e "${files}n" | tail -5

解释:

-e标志使 echo 能够解释转义,例如本示例中的转义n

n本身就是"新行"的转义序列。因此,它所做的只是在回显变量之后添加一个新行。

${ }称为支撑扩展。由于我将字符串放在引号中,因此${}会将变量扩展到字符串。

即使它被请求用于 BASH,我只是在这里放了 ZSH 行

echo dirname/*(.om[1,5]) dirname/*(.om[-5,-1])

这将返回包含 5 个最旧文件和 5 个最新文件的files列表(基于修改时间)。其他基于ls -lrth的解决方案可能会返回目录、链接、管道或其他任何内容。

你可以用任何东西替换echo,但是你要求一种查找文件的方法,因此ZSH中的正确答案是上面的glob(没有echo)

它的工作原理是这样的:

  • dirname/*:取所有数学字符串
  • (: 开球说明符
  • .:仅返回普通文件
  • om:根据修改时间排序
  • [1,5]返回前五个或[-5,-1]返回后五个
  • ): 关闭全局说明符

更多关于 zsh 通配的信息可以在这里找到: http://www.bash2zsh.com/zsh_refcard/refcard.pdf

最新更新