2015-10-30 84 views
2

我正在查看一些shell代碼,以獲取目錄中文件數量的計數。它讀取:「ls -1 path」中的-1是什麼意思?

COUNT=$(ls -1 ${DIRNAME} | wc -l) 

什麼是-1部分是什麼意思?在任何其他問題中,我都找不到任何有關此問題的任何內容,只是將引用反覆遍歷目錄中的文件,而這不是我正在查看的內容。另外,從命令中刪除它似乎沒有效果。

+2

發射一個線。 –

+1

...其中,當輸出不是終端時是默認值,但顯式比隱式更好。 –

+0

@CharlesDuffy啊,這很有道理,謝謝。解釋爲什麼當我刪除它時沒有任何變化。 –

回答

9
COUNT=$(ls -1 ${DIRNAME} | wc -l) 

...是一個目錄來算文件馬車道:ls -1告訴ls不要把在一行多個文件;確保wc -l然後,通過計數行,計數文件。

現在,讓我們說話 「馬車」:

  • 文件名可以包含文字換行。 ls版本如何處理這個問題是由實現定義的;某些版本可能會對這些文件進行重複計算(GNU系統不會,但我不希望對在嵌入式路由器上隨機發布的busybox進行下注)。
  • 未引用擴展${DIRNAME}允許目錄名稱在傳遞到ls之前進行字符串拆分和全局展開,因此如果名稱包含空格,它可以變成多個參數。這應該是"$DIRNAME""${DIRNAME}"

...也是,這是低效的,因爲它調用多個外部工具(lswc)做某事的外殼可以在內部管理。


如果你想要的東西更強大的,這個版本將與所有POSIX殼工作:

count_entries() { set -- "${1:-.}"/*; if [ -e "$1" ]; then echo "$#"; else echo 0; fi; } 
count=$(count_entries "$DIRNAME") ## ideally, DIRNAME should be lower-case. 

...或者,如果你希望它是執行的速度更快(不需要子shell)見下面的(只針對bash)的:

# like above, but write to a named variable, not stdout 
count_entries_to_var() { 
    local destvar=$1 
    set -- "${2:-.}"/* 
    if [[ -e "$1" || -L "$1" ]]; then 
    printf -v "$destvar" %d "$#" 
    else 
    printf -v "$destvar" %d 0 
    fi 
} 
count_entries_to_var count "$DIRNAME" 

...或者,如果你的目標bash和不想與函數打擾,你可以使用一個數組:

files=("$DIRNAME"/*) 
if [[ -e "${files[0]}" || -L "${files[0]}" ]]; then 
    echo "At least one file exists in $DIRNAME" 
    echo "...in fact, there are exactly ${#files[@]} files in $DIRNAME" 
else 
    echo "No files exist in $DIRNAME" 
fi 

最後 - 如果你要處理太大,不適合在內存中的文件名列表,你有GNU find,請考慮使用:

find "$DIRNAME" -mindepth 1 -maxdepth 1 -printf '\n' | wc -l 

...這避免將所有名稱放入流中(從而生成一個可以簡單測量長度(以字節爲單位)而不是行數的流,如果這樣選擇的話)。

+2

你能解釋一下這個問題嗎?並提出一個安全的方法來做到這一點? –

+1

@JonathanLeffler,...完成。 :) –

+1

謝謝。我觀察到,如果由'$ {DIRECTORY}'表示的字符串很長,那麼在極端情況下,您可能會遇到問題,因爲名稱太長。我注意到你可以用'set --'設置更長的參數列表,而不是你用後面的外部命令來處理:'set - $(perl -e'for $ i(1..1000){printf「%.5d - %s.txt \ n「,$ i,」ABC「x 256;}'); echo $#; echo「$ @」|廁所; al「$ @」| wc' 在Mac OS X 10.10 Yosemite上,這會產生'1000'(來自'echo $#'),'1 1000 779000'來自'echo'$ @「| wc'和'argument list too long' from processing'al | wc'(後跟3個來自'wc'的零)。 –

2

爲了補充Charles Duffy's excellent answer
有一個邊緣情況下,他的回答不包括:如果第一個目錄項恰好是一個破符號鏈接,與-e測試的水珠擴張是不夠的,因爲Bash總是在將存在測試應用於符號鏈接的目標 - 在符號鏈接斷開的情況下,根據定義不存在。換句話說:對於破壞的符號鏈接,-e將指示虛假,即使鏈接本身存在。因此,一個完全可靠的解決方案必須使用類似[[ -e "$1" || -L "$1" ]]
-L測試,如果它的參數是一個符號連接,是否破損或不。)

這裏有一個稍微bash替代(使用一個子shell):

count=$(shopt -s nullglob; entries=(*); echo "${#entries[@]}") 
  • shopt -s nullglob確保如果沒有任何匹配,模式將擴展爲空字符串。
  • entries=(*)收集數組中的所有匹配項(在當前目錄中)
  • echo "${#entries[@]}"輸出元素數組數。
  • 由於不涉及外部實用程序,因此此命令爲而不是,但受限於getconf ARG_MAX限制,因此應使用大型目錄。

注意是否上述計數隱藏.*)項目,以及依賴於dotglob選項的狀態。 這是很容易然而,建立固定的隱藏物品-包括有或沒有邏輯到命令:

明確包括隱藏物品:

count=$(shopt -s nullglob dotglob; entries=(*); echo "${#entries[@]}") 

明確排除隱藏物品:

count=$(shopt -s nullglob; shopt -u dotglob; entries=(*); echo "${#entries[@]}") 

這是可能的包裹所有上述的以靈活的功能

每個文件輸出的
countEntries [<dir>] ... counts based on current state of the `dotglob` option 
countEntries <dir> 0 ... counts non-hidden entries only 
countEntries <dir> 1 ... counts all entries, including hidden ones 
#!/usr/bin/env bash 

# SYNOPSIS 
# countEntries [<dir> [<includeHidden>]] 
# DESCRIPTION 
# <dir> defaults to . 
# <includeHidden> default to the current state of `shopt dotglob`; 
# a value of 0 explicitly EXcludes, 1 explicity INcludes hidden items. 
countEntries() (# Run entire function in subhell. 
    local dir=${1:-.} includeHidden=$2 entries 
    shopt -s nullglob 
    case $includeHidden in 
    0) # EXclude hidden entries 
     shopt -u dotglob 
     ;; 
    1) # INclude hidden entries 
     shopt -s dotglob 
     ;; 
    # Otherwise: use *current state* of `dotglob` 
    esac 
    entries=("$1"/*) # Collect in array 
    echo "${#entries[@]}" # Output count. 
)