代码之家  ›  专栏  ›  技术社区  ›  Homunculus Reticulli

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

  •  0
  • Homunculus Reticulli  · 技术社区  · 8 年前

    我有以下命令:

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

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

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

    4 回复  |  直到 8 年前
        1
  •  2
  •   John Kugelman Michael Hodel    8 年前

    始终引用变量扩展以防止分词和全局搜索。当你离开的时候 $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"
    
        2
  •  2
  •   F. Hauri - Give Up GitHub    8 年前

    头尾并拢

    (无需将前一个命令的整个输出存储到一个变量中)

    注:沿着这一点,我将使用前4行和后4行作为样本使用 seq 1 100.. ,但使用 ls -lhrt dirname .

    第一种方式,使用 head tail 连续地

    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
    

    给出错误的答案。

    缓冲 但是 允许您使用无缓冲输入:

    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
    

    必须完成这项工作。

    甚至,如果你想玩 还有一个很大的变量:

    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

    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;'
    
        3
  •  1
  •   NullDev Kevin Fisher    8 年前

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

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

    说明:

    这个 -e 标志使echo能够解释转义,例如 \n 在本例中。

    \n

    ${ } 被称为 Brace Expansion ${}

        4
  •  0
  •   kvantour    8 年前

    虽然它是为BASH请求的,但我只是在这里放了ZSH行

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

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

    你可以替换 echo 回响 )

    其工作原理如下:

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

    有关zsh globbing的更多信息,请参见: http://www.bash2zsh.com/zsh_refcard/refcard.pdf

    推荐文章