![排除列表中最后修改且文件扩展名以 .gz 结尾的最后 N 行](https://linux22.com/image/208337/%E6%8E%92%E9%99%A4%E5%88%97%E8%A1%A8%E4%B8%AD%E6%9C%80%E5%90%8E%E4%BF%AE%E6%94%B9%E4%B8%94%E6%96%87%E4%BB%B6%E6%89%A9%E5%B1%95%E5%90%8D%E4%BB%A5%20.gz%20%E7%BB%93%E5%B0%BE%E7%9A%84%E6%9C%80%E5%90%8E%20N%20%E8%A1%8C.png)
我想find
在 AIX 上使用该命令来排除以 结尾的文件.gz
,并且它还必须从列表中排除最后 2 行。例如,在目录中,我有:
shop14_0_Log0002019754.gz
shop14_0_Log0002019755.gz
shop14_0_Log0002019756.gz
shop14_0_Log0002019757
shop14_0_Log0002019758.gz
shop14_0_Log0002019759.gz
shop14_0_Log0002019760.gz
shop14_0_Log0002019761.gz
shop14_0_Log0002019762
我想通过仅检索未压缩的文件来获得如下所示的输出,但排除底部的最后 2 个文件:
输出命令必须达到:
shop14_0_Log0002019757
我可以使用该ls
命令排除最后两行,但是如何通过排除名称以 结尾的文件来做到这一点.gz
?我正在努力寻找 AIX/UNIX 中的方法:
ls -ltr | awk '{print $9} | sed '$d' | sed '$d'
使用find
,我能够通过.gz
从列表中排除来获得未压缩的文件列表,但它包括我不想要的最后两个文件:
find . -type f ! -name '*\.gz' -print
上述find
命令返回:
./shop14_0_Log0002019757
./shop14_0_Log0002019762
该文件shop14_0_Log0002019762
应从列表中排除,如果shop14_0_Log0002019761
也未压缩,则也必须从列表中排除。
要排除的“最后 2”条目根据文件修改时间排序。我的最终目标是压缩未压缩的文件。
我怎样才能做到这一点?
答案1
如果您使用的是 bash,并且 100% 确定您的文件名永远不会包含换行符或空格,您可以执行以下操作:
shopt -s extglob
ls -t !(*gz) | tail -n +3 | while IFS= read -r file; do gzip "$file"; done
它shopt -s extglob
启用了扩展的通配符,这给了我们!(*gz)
“不以 gz 结尾”。然后,通过使用ls -t
,我们按修改时间排序,最新的在前,这tail -n +3
意味着“从第三行开始打印所有内容”,因此将跳过前两个文件。最后,将其通过管道传送while
到gzip
文件的循环。或者,您也可以这样做:
gzip $(ls -t !(*gz) | tail -n +3)"
或者
ls -t !(*gz) | tail -n +3 | xargs gzip
请注意,这仅在您可以确定文件名正确的情况下才有效。看https://mywiki.wooledge.org/ParsingLs为什么ls
不建议解析 的输出。
答案2
目前尚不清楚是否应该.gz
在剥离最后 2 个文件之前或之后排除文件。因为如果之前,那么只有两个文件保留在您的示例中,并且都应该被省略,因此输出将为空,但您包含shop14_0_Log0002019757
.我以“之后”的假设来解决任务。
解决方案1,重击:
#!/bin/bash
files=(*)
newest_1=${files[0]}
newest_2=${files[0]}
for f in "${files[@]}"; do
if [[ $f -nt $newest_1 ]]; then
newest_2=$newest_1
newest_1=$f
elif [[ $f -nt $newest_2 ]]; then
newest_2=$f
fi
done
if [[ $newest_1 == "$newest_2" ]]; then
filenames=$newest_1
else
filenames="${newest_2}\n${newest_1}"
fi
echo -e "$filenames" | sed '/\.gz$/d'
解决方案2:使用 AIX 默认情况下没有的 GNU 实用程序。但它可以作为方法的演示很有用。
find . -maxdepth 1 ! -name '.' -printf "%A@ %f\n" | sort -g | head -n -2 | cut -d' ' -f2 | sed '/\.gz/d'
解释
find .
- 在当前目录中查找-maxdepth 1
- 不递归,仅 1 级! -name '.'
- 排除当前目录条目 (.
)-printf "%A@ %f\n" |
- 输出文件从 1970 年起的最后访问时间(以秒为单位),包含小数部分和文件名sort -g |
- 按浮点数排序head -n -2 |
- 输出除最后 2 行之外的所有行cut -d' ' -f2 |
- 仅通过剪切第一列来保留文件名sed '/\.gz$/d'
- 删除.gz
文件。
答案3
假设您喜欢使用 find 命令,这可能会解决您的问题:
find . -type f ! -name '*\.gz' -print | awk '{Q[N++]=$0; N=N%3; if (Q[N]!="") {print Q[N]}}
awk 命令填充第 n 个长度的数组 Q
- 问[N++]
通过数组模 3 前进(较长的数组可跳过更多元素)
- N=N%3
并打印之前填充的 2 个插入的条目(因此忽略最后两个)。当数组尚未完全初始化时,if 会跳过第一个插入操作
123 123 123 123 123 ...
^^ ^^ ^ ^ ^^ ^^
|| || | | || ||
io io O i iO iO
nu nu U n nU nU
t t T T T
如果您更喜欢使用“ls”,则可以将 $0 更改为第 n 个元素(在这种情况下,文件名中不能有空格)。请检查,因为我不确定我是否完全理解您问题的极端情况。
答案4
使用 ksh93 shell(这是较新的 AIX 版本的标准)来支持数组,并假设文件按修改时间顺序展开(即文件名的顺序表示与修改时间戳相同的顺序),您可以使用一个数组获取整个文件列表(对于稍后提取“最后两个”很有用),然后使用另一个数组查找所有未压缩的文件。通过循环遍历未压缩的文件列表并删除与之前的“最后两个”匹配的所有文件来收集最终的文件列表。
#!/usr/bin/ksh93
files=(*)
uncompressed=( !(*.gz) )
for index in "${!uncompressed[@]}"
do
if [[ "${uncompressed[index]}" == "${files[-1]}" ]] || \
[[ "${uncompressed[index]}" == "${files[-2]}" ]]
then
unset -v 'uncompressed[index]'
fi
done
# echo gzip "${uncompressed[@]}"
最后你有一个名为“uncompressed”的数组;您可以按所示压缩这些文件名(删除echo
)。