【问题标题】:How to print last directory inside every parent directory如何在每个父目录中打印最后一个目录
【发布时间】:2019-04-08 12:35:04
【问题描述】:

我有一组目录和子目录如下。

aaa/180809_1047
aaa/180915_0055
aaa/181012_1545
aaa/xyz
bbb/180809_1047
bbb/180915_0055
bbb/181012_1545
bbb/181105_0000
bbb/xyz
.
.
.
zzz/180821_1555
zzz/181004_2355
zzz/xyz

大部分子目录都是 YYMMDD_HHMM 格式。在这里,我试图从每个父目录打印格式为 YYMMDD_HHMM 的子目录(最新)。以下是我的要求。

输出:

aaa/181012_1545
bbb/181105_0000
zzz/181004_2355

我使用的 find 命令如下:

find ./*/ -type d -maxdepth 1 -mindepth 1 -name "???????????" | sort -u | tail -1

"???????????" --> 这是打印符合格式 YYMMDD_HHMM 的目录。

在这个命令之后,我只得到了。

zzz/181004_2355 

【问题讨论】:

    标签: linux shell perl unix


    【解决方案1】:

    我会在 bash 中使用 for 循环,而不是使用 find

    老实说,我不记得 bash 是否对 glob 的处理顺序做出任何承诺。因此,使用您包含的示例日期,这是一个使用[[ 比较文件的单行:

    $ declare -A last=(); for a in *; do for b in $a/[0-9]*/; do [[ $b > $last[$a] ]] && last[$a]=$b; done; done; declare -p last
    declare -A last=([bbb]="bbb/181105_0000/" [zzz]="zzz/181004_2355/" [aaa]="aaa/181012_1545/" )
    

    请注意,这里的限制模式是$a/[0-9]*/,这对于您的示例数据来说已经足够了。当然,您可以根据需要限制这一点,使用字符类并消除 glob。

    还要注意,此模式中的尾随 / 可确保您只匹配目录。这将在$last 数组中的每个结果的末尾放置一个/。如果需要,您可以进行后处理:

    $ for i in "${!last[@]}"; do last[$i]="${last[$i]%/}"; last[$i]="${last[$i]#*/}"; done
    $ declare -p last
    declare -A last=([bbb]="181105_0000" [zzz]="181004_2355" [aaa]="181012_1545" )
    

    为了便于阅读,这里将单行拆分为多行。 :)

    # Create an associative array. Requires bash 4+.
    declare -A last=()
    
    # Step through the top-level directories
    for a in *; do
      # Step through the second level directories
      for b in "$a"/[0-9]*/; do
        # Compare and record as required
        [[ $b > $last[$a] ]] && last[$a]="$b"
      done
    done
    
    # Print the result
    declare -p last
    

    【讨论】:

    • 本机全局排序取决于 LOCALE 环境设置,LC_ALL 覆盖。所以它就是其中之一——这取决于事情......
    【解决方案2】:

    您正在使用tail -1 跟踪命令的输出。所以你只会得到最后一行。 :)

    除了你的命令看起来是正确的。

    其他注意事项:

    1. 您可以不使用 glob 编写 find .,因为 find 默认情况下是递归的
    2. 如果您需要,??????????? 可能会更加严格。 ??????_???? 或使用 [[:digit:]] 将是选项。

    【讨论】:

    • 我想要获得最后一行的意图,因为我使用排序来获得最后的最新子目录。由于 tail -1 只为我获取所有打印的最后一行(在本例中为 zzz/181004_2355),如何获取所有最新的子目录,如下所示?
      aaa/181012_1545
      bbb/181105_0000
      zzz/181004_2355
    【解决方案3】:

    使用查找、排序、awk:

    find -name '??????_????' -type d | sort -r | awk -F'/' '{if(!s[$(NF-1)]++) print $0}
    

    【讨论】:

      【解决方案4】:

      使用sort -utail -1 的想法很好,并且在与同一父目录中的子目录列表一起使用时会起作用。 -u 将删除重复项,但这不是必需的,因为同一父目录中的 2 个子目录不能具有相同的名称。

      ? 表示任何字符;可以使用更严格的[0-9] 来选择单个数字。

      试试这个:

      find . -maxdepth 1 -type d  -print0 | xargs -0 sh -c '
       for dir ; do 
         find "${dir}" -maxdepth 1 -type d \
          -name '[0-9][0-9][0-1][0-9][0-3][0-9]_[0-2][0-9][0-6][0-9]' | sort | tail -1
       done' dummy | sort
      

      对于在第一级找到的每个目录(第一个find . -maxdepth 1 ...):

      • 匹配模式的所有子目录 [0-9][0-9][0-1][0-9][0-3][0-9]_[0-2][0-9][0-6][0-9] 已列出(第二个 find
      • 只打印最新的(感谢sorttail 命令)

      -print0-0 参数与 sh -cfor statement 一起使用,以使命令行对具有特殊字符的文件名(例如 line break)具有鲁棒性。

      dummy 未使用,但它是强制性的,请参阅man sh

      测试

      mkdir -p aaa/180809_1047 aaa/180915_0055 aaa/181012_1545 aaa/xyz \
      bbb/xyz bbb/180809_1047 bbb/180915_0055 bbb/181012_1545 bbb/181105_0000 \
      zzz/xyz zzz/180821_1555 zzz/181004_2355
      
      find . -maxdepth 1 -type d  -print0 | xargs -0 sh -c '
        for dir ; do 
          find "${dir}" -maxdepth 1 -type d \
           -name '[0-9][0-9][0-1][0-9][0-3][0-9]_[0-2][0-9][0-6][0-9]' | sort | tail -1
        done' dummy | sort
      
      
      ./bbb/181105_0000
      ./aaa/181012_1545
      ./zzz/181004_2355
      

      【讨论】:

        猜你喜欢
        • 2018-11-23
        • 1970-01-01
        • 2019-11-13
        • 1970-01-01
        • 2018-06-28
        • 2022-01-06
        • 1970-01-01
        • 1970-01-01
        • 2010-11-06
        相关资源
        最近更新 更多