2016-08-13 67 views
1

我想要一個將返回一個表的bash命令,其中每行是可讀的文件大小,行數和文件名。該表應按文件大小排序。人類可讀的文件大小和行數

我一直在努力做到這一點使用的du -hswc -lsort -hfind的組合。

下面是我在哪裏:

find . -exec echo $(du -h {}) $(wc -l {}) \; | sort -h 
+0

告訴我們你得到了什麼 – eckes

+0

@eckes用非工作代碼編輯 – Hatshepsut

回答

1

你的做法短期下跌不僅是因爲外殼擴展您的命令替換($(...)前面,但更根本的,因爲你不能傳遞外殼命令行直接find

find-exec動作只能調用外部公用事業字面參數 - 支持的唯一非文字形參是表示手頭的文件名(多個){}

choroba's answer修復您通過眼前的問題在每次迭代中調用一個單獨的殼例如,爲了外殼命令來執行作爲字符串參數-exec bash -c '...' \;)被傳遞哪個。
雖然這工作(假設你通過{}值作爲參數而不是在命令行字符串嵌入它),它也是相當低效,因爲多個子進程創建每個輸入文件

(雖然辦法有find通(典型地)所有輸入文件到一個(典型地)調用指定的外部工具的 - 即與終止子+而非\;,這是這裏由於傳遞的命令行的性質的選項。)

一種高效且健壯的[1] 執行該孩子的數目最小化處理創建應該是這樣的:

注:我假設這裏GNU公用事業,由於使用的head -n -1sort -h
另外,我限制find的輸出文件只(而不是目錄),因爲wc -l僅適用於文件

paste <(find . -type f -exec du -h {} +) <(find . -type f -exec wc -l {} + | head -n -1) | 
    awk -F'\t *' 'BEGIN{OFS="\t"} {sub(" .+$", "", $3); print $1,$2,$3}' | 
    sort -h -t$'\t' -k1,1 
  • 使用注意事項的-exec ... +而非-exec ... \;,這確保了通常所有輸入的文件名被傳送到單個調用到外部實用程序(如果不是所有的文件名適合於單一命令行,調用有效批處理以儘可能少的調用)。

  • wc -l {} +總是輸出一條摘要行,其中head -n -1去掉,但也在每行數後輸出文件名。

  • paste組合來自每個命令(其各自的輸入端通過取代。<(...)的處理被設置)成單個輸出流的行。

  • awk命令然後從每行的末尾去除源自wc的外部文件名。

  • 最後,sort命令由第一排序結果(-k1,1)製表符分隔(-t$'\t')柱通過人類可讀的數字(-h),如數字,du -h輸出(例如,1K)。


[1]與任何取向處理,嵌入式換行符文件名不支持,但我不認爲這是一個現實世界的問題。

0

的問題是,你的shell解釋$(...),所以find沒有得到他們。轉義它們對(\$\(du -h {}\))無效,因爲它們成爲命令的正常參數,而不是命令替換。

爲了它們解釋爲命令替換是調用一個新的外殼,無論是直接

find . -exec bash -c 'echo $(du -h {}) $(wc -l {})' \; | sort -h 

,或者通過創建腳本,並從find調用它。

+0

是否有可能只顯示文件名一次? – Hatshepsut

+1

@Hatshepsut:當然,使用'wc -l <​​{}'。 – choroba

+0

不幸的是,這將打破文件名與嵌入式元字符,如空格(並可能與嵌入式globbing字符)。 要解決這個問題,你必須使用'find。 -exec bash -c'echo'$(du -h「$ 1」)$(wc -l <​​「$ 1」)「' - {} \; |然而,即使這解決了OP的直接問題並且非常短,但這種方法對於大型輸入文件集將執行得不好,因爲它會創建多個子進程_per filename_。 – mklement0

1

好吧,我也試過用find/-exec,但逃跑是地獄。具有外殼功能,它的工作原理非常簡單:

#!/bin/bash 
function dir 
{ 
    du=$(du -sh "$1" | awk '{print $1}') 
    wc=$(wc -l < "$1") 
    printf "%10s %10s %s\n" $du $wc "${1#./}" 
} 

printf "%10s %10s %s\n" "size" "lines" "name" 
OIFS=$IFS; IFS="" 
find . -type f -print0 | while read -r -d $'\0' f; do dir "$f"; done 
IFS=$OIFS 

使用basishm閱讀它甚至可以使用nul終止符來保證安全。 IFS需要避免讀取來截斷文件名中的尾隨空白。

順便說一下:$'\0'實際上並不奏效(與''相同) - 但它使意圖清晰。

輸出示例:

 size  lines name 
     156K  708 sash 
     16K   64 hostname 
     120K  460 netstat 
     40K  110 fuser 
     644K  1555 dir/bash 
     28K   82 keyctl 
     2.3M  8067 vim