排除列表中最后修改且文件扩展名以 .gz 结尾的最后 N 行

排除列表中最后修改且文件扩展名以 .gz 结尾的最后 N 行

我想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意味着“从第三行开始打印所有内容”,因此将跳过前两个文件。最后,将其通过管道传送whilegzip文件的循环。或者,您也可以这样做:

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)。

相关内容