【问题标题】:Bash: Reading a column from ls -lBash:从 ls -l 读取一列
【发布时间】:2016-01-12 19:21:04
【问题描述】:

对于 uni 的问题,我需要获取一系列目录中 5 个最大文件的文件大小和文件名。为此,我使用了两个函数,一个使用 ls -l 加载所有内容(我意识到从 ls 解析信息不是一个好方法,但这个特定问题表明我不能使用 find、locate 或 du) .然后将 ls 输出中的每一行发送到另一个函数,该函数使用 awk 应该提取文件大小和文件名并将其存储到数组中。相反,我似乎越来越 awk 试图从 ls 打开每一列以进行阅读。 代码如下:

function addFileSize {
    local y=0
    local curLine=$1
    if [[ -z "${sizeArray[0]}" ]]; then
        i=$(awk '{print $5}' $curLine)
        nameArray[y]=$(awk '{print $9}' $curLine)
    elif [[ -z "${sizeArray[1]}" ]]; then
        i=$(awk '{print $5}' $curLine)
        nameArray[y]=$(awk '{print $9}' $curLine)
    elif [[ -z "${sizeArray[2]}" ]]; then
        i=$(awk '{print $5}' $curLine)
        nameArray[y]=$(awk '{print $9}' $curLine)
    elif [[ -z "${sizeArray[3]}" ]]; then
        i=$(awk '{print $5}' $curLine)
        nameArray[y]=$(awk '{print $9}' $curLine)
    elif [[ -z "${sizeArray[4]}" ]]; then
        i=$(awk '{print $5}' $curLine)
        nameArray[y]=$(awk '{print $9}' $curLine)
    fi  

    for i in "${sizeArray[@]}"; do
        echo "$(awk '{print $5}' $curLine)"
        if [[ -z "$i" ]]; then
            i=$(awk '{print $5}' $curLine)
            nameArray[y]=$(awk '{print $9}' $curLine)
            break
        elif [[ $i -lt $(awk '{print $5}' $curLine) ]]; then
            i=$(awk '{print $5}' $curLine)
            nameArray[y]=$(awk '{print $9}' $curLine)
            break
        fi
        let "y++"
    done
    echo "Name Array:"
    echo "${nameArray[@]}"
    echo "Size Array:"
    echo "${sizeArray[@]}"
}

function searchFiles {
    local curdir=$1
    for i in $( ls -C -l -A $curdir | grep -v ^d | grep -v ^total ); do # Searches through all files in the current directory
        if  [[ -z "${sizeArray[4]}" ]]; then
            addFileSize $i
        elif [[ ${sizeArray[4]} -lt $(awk '{print $5}' $i) ]]; then
            addFileSize $i
        fi
    done
}

任何帮助将不胜感激,谢谢。

【问题讨论】:

  • 在每个目录上使用stat -c'%s %n' * 之类的东西怎么样?一旦你连接所有你可以sort -rn | head -5.
  • 你可以使用 GNU stat 吗?

标签: linux bash shell awk


【解决方案1】:

如果问题是专门针对解析的,那么 awk 可能是一个不错的选择(尽管 ls 的输出很难可靠地解析)。同样,如果问题与使用数组有关,那么您的解决方案应该专注于这些。

但是,如果问题是为了鼓励您学习可用的工具,我建议:

  • stat 工具打印有关文件的特定信息(包括大小)
  • sort 工具对输入行重新排序
  • headtail 工具打印输入的第一行和最后一行
  • 并且您的 shell 还可以执行 路径名扩展 以列出匹配 glob 通配符模式的文件,例如 *.txt

想象一个包含一些不同大小文件的目录:

10000000声音/音乐会.wav 1000000声音/歌曲.wav 100000声音/ding.wav

您可以使用路径名扩展来找到他们的名字:

$ echo sound/*
sound/concert.wav sound/ding.wav sound/song.wav

您可以使用 stat 将名称转换为 size:

$ stat -f 'This one is %z bytes long.' sound/ding.wav
This one is 100000 bytes long.

像大多数 Unix 工具一样,stat 无论您提供一个参数还是多个参数都一样:

$ stat -f 'This one is %z bytes long.' sound/concert.wav sound/ding.wav sound/song.wav
This one is 10000000 bytes long.
This one is 100000 bytes long.
This one is 1000000 bytes long.

(查看man stat 以获取有关%z 的参考信息以及您可以打印的其他内容。文件的Name 特别有用。)


现在您有了一个文件大小列表(希望您也保留了它们的名称)。您如何找到最大的尺寸?

在排序列表中找到最大的项目比在未排序列表中容易得多。为了感受一下,想想如何在这个未排序的列表中找到最高的两项:

1234 5325 3243 4389 5894 245 2004 45901 3940 3255

如果列表已排序,您确实可以很快找到最大的项目:

245 1234 2004 3243 3255 3940 4389 5325 5894 45901

Unix sort 实用程序获取输入行并将它们从最低到最高输出(或以 rsort -r 的相反顺序)。

它默认按字符排序,这对单词非常有用(“apple”在“balloon”之前)但对于数字不太好(“10”在“9”之前)。您可以使用sort -n 激活n数字排序。


一旦你有一个排序的行列表,你可以使用 head 工具打印第一行,或者使用 tail 工具打印最后一行。

用于拼写检查的(已排序的)单词列表的前两项:

$ head -n 2 /usr/share/dict/words
A
a

最后两项:

$ tail -n 2 /usr/share/dict/words
Zyzomys
Zyzzogeton

通过这些部分,您可以组合解决“在 dir1、dir2、dir3 中找到五个最大文件”的问题:

stat -f '%z %N' dir1/* dir2/* dir3/* |  
     sort -n |  
     tail -n 5  

或“在dir1、dir、dir3、dir4、dir5中找到最大的文件”的解决方案:

for dir in dir1 dir2 dir3 dir4 dir5; do  
    stat -f '%z %N' "$dir"/* |  
        sort -n |  
        tail -n 1  
done

【讨论】:

  • 不错的综合答案,尽管我不确定您为什么使用剧透标签。我建议将它们更改为普通代码块。
  • 这个问题是一个学校问题,所以我觉得鼓励学生在“检查答案页面”之前停下来尝试自己的解决方案具有教学价值。至少有两个人更喜欢普通的代码块,所以我已经适当地编辑了我的答案。
【解决方案2】:

不使用findlocatedu,您可以对每个目录执行以下操作:

    ls -Sl|grep ^\-|head -5|awk '{printf("%s %d\n", $9, $5);}'

它按大小列出所有文件,过滤掉目录,获取前 5 个文件,并打印文件名和大小。在 bash 中为每个目录添加一个循环。

【讨论】:

    【解决方案3】:

    使用ls -S 按大小排序,通过head 获得前五名,通过sed 将多个空格压缩为一个,然后通过cut 获得大小和文件名字段。

    robert@habanero:~/scripts$ ls -lS |头-n 5 | sed -e 's/ / /g' |剪切 -d " " -f 5,9

    32K xtractCode.pl

    29K tmd55.pl

    24K tagebuch.pl

    14K 备份

    只需将目录指定为初始 ls 的参数。

    【讨论】:

      【解决方案4】:

      这将是另一种选择。 Ctrl+V+I 是如何从命令行插入标签。

      ls -lS dir1 dir2 dir3.. | awk 'BEGIN{print "Size""Ctrl+V+I""Name"}NR <= 6{print $5"Ctrl+V+I"$9}'
      

      【讨论】:

      • @HRusby 你需要使用数组吗?
      • 如果您想问 OP 一些事情,那么您应该在他们的问题下方进行 - 除非他们也对您的回答发表了评论,否则他们不会收到此评论的通知。
      • @TomFenech 我必须有 50 声望才能问 OP
      【解决方案5】:

      如果您不能使用find locatedu,仍然有一个直接的选项来获取文件大小,而无需借助ls 解析:

      size=$(wc -c < "$file")
      

      wc 足够聪明,可以检测 STDIN 上的文件并调用 stat 来获取大小,因此它的运行速度同样快。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-03-17
        • 1970-01-01
        • 1970-01-01
        • 2011-01-30
        • 1970-01-01
        • 2012-04-23
        • 2011-01-23
        • 1970-01-01
        相关资源
        最近更新 更多