2011-05-04 41 views
75

在Bash中,我想創建一個函數,返回匹配特定模式的最新文件的文件名。例如,我有一個文件目錄:Bash函數來尋找最新的文件匹配模式

Directory/ 
    a1.1_5_1 
    a1.2_1_4 
    b2.1_0 
    b2.2_3_4 
    b2.3_2_0 

我想要以'b2'開頭的最新文件。我如何在bash中做到這一點?我需要在我的~/.bash_profile腳本中有這個。

+1

有關更多答案提示,請參閱http://superuser.com/questions/294161/unix-linux-find-and-sort-by-date-modified。排序是獲取最新文件的關鍵步驟 – 2016-12-04 13:06:33

回答

139

ls命令有一個參數-t按時間排序。然後你可以用head -1抓住第一個(最新)。

ls -t b2* | head -1 

但要注意:Why you shouldn't parse the output of ls

我個人的看法是:解析ls只有危險的時候文件名可以包含一些奇怪的字符,如空格或換行符。如果你可以保證文件名不會包含有趣的字符,那麼解析ls是相當安全的。

如果你正在開發一個腳本,許多人在很多系統上運行,許多不同的情況下,我非常推薦不要分析ls

這裏是如何做到這一點「權利」:How can I find the latest (newest, earliest, oldest) file in a directory?

unset -v latest 
for file in "$dir"/*; do 
    [[ $file -nt $latest ]] && latest=$file 
done 
+5

對其他人的注意事項:如果您正在爲目錄執行此操作,則需要向ls添加-d選項,例如'ls -td |頭-1' – 2012-12-27 19:53:27

+4

[解析LS](http://mywiki.wooledge.org/ParsingLs)鏈接表示不這樣做,並建議在[BashFAQ 99](http://mywiki.wooledge.org/BashFAQ/099)。我正在尋找一個1-liner,而不是防彈包含在腳本中,所以我會繼續像@lesmana那樣不安分地解析ls。 – Eponymous 2014-06-05 00:07:37

+0

@同名:如果你正在尋找一個沒有使用脆弱的'ls','printf'%s \ n「b2 * |頭-1'將爲你做。 – 2016-12-01 04:27:02

2

不尋常的文件名(如含有有效\n角色可以肆虐用這種分析的一個文件,這裏有一個辦法做到這一點的的Perl:

perl -le '@sorted = map {$_->[0]} 
        sort {$a->[1] <=> $b->[1]} 
        map {[$_, -M $_]} 
        @ARGV; 
      print $sorted[0] 
' b2* 

這是一個Schwartzian transform使用有

+0

可能schwartz與你同在! – 2016-06-24 17:45:57

+0

這個答案可能工作,但我不會信任它,因爲糟糕的文檔。 – 2016-12-04 12:56:27

+1

糟糕的文檔?它帶有一個wikipage ... – 2017-04-12 23:20:47

4

這是可能實現的。需要Bash功能:

# Print the newest file, if any, matching the given pattern 
# Example usage: 
# newest_matching_file 'b2*' 
# WARNING: Files whose names begin with a dot will not be checked 
function newest_matching_file 
{ 
    # Use ${1-} instead of $1 in case 'nounset' is set 
    local -r glob_pattern=${1-} 

    if (($# != 1)) ; then 
     echo 'usage: newest_matching_file GLOB_PATTERN' >&2 
     return 1 
    fi 

    # To avoid printing garbage if no files match the pattern, set 
    # 'nullglob' if necessary 
    local -i need_to_unset_nullglob=0 
    if [[ ":$BASHOPTS:" != *:nullglob:* ]] ; then 
     shopt -s nullglob 
     need_to_unset_nullglob=1 
    fi 

    newest_file= 
    for file in $glob_pattern ; do 
     [[ -z $newest_file || $file -nt $newest_file ]] \ 
      && newest_file=$file 
    done 

    # To avoid unexpected behaviour elsewhere, unset nullglob if it was 
    # set by this function 
    ((need_to_unset_nullglob)) && shopt -u nullglob 

    # Use printf instead of echo in case the file name begins with '-' 
    [[ -n $newest_file ]] && printf '%s\n' "$newest_file" 

    return 0 
} 

它只使用Bash內建函數,並且應該處理名稱中包含換行符或其他不尋常字符的文件。

+0

你可以使用'nullglob_shopt = $(shopt -p nullglob)',然後''nullglob''把'nullglob'放回原來的狀態。 – 2014-11-05 21:14:11

+0

@gniourf_gniourf使用$(shopt -p nullglob)的建議是一個很好的建議。我通常儘量避免使用命令替換('$()'或反引號),因爲它很慢,特別是在Cygwin下,即使命令只使用內置函數。另外,運行命令的子shell上下文有時會導致它們以意想不到的方式運行。我也嘗試避免在變量中存儲命令(比如'nullglob_shopt'),因爲如果你得到的變量值不對,會發生很糟糕的事情。 – pjh 2014-11-06 20:25:55

+0

我很欣賞對細節的關注,如果忽略了這些細節,可能會導致模糊的失敗。謝謝! – 2016-07-31 18:32:07

2

有一個更有效的方法來實現這一點。請看下面的命令:

find . -cmin 1 -name "b2*" 

這個命令在與「B2 *」通配符搜索產生恰好一分鐘前的最新文件。如果你想從最近兩天的文件,那麼你會更好使用以下命令:「」

find . -mtime 2 -name "b2*" 

的代表當前目錄。 希望這有助於。

+5

這實際上並沒有找到「最新的文件匹配模式」......它只是找到所有匹配模式的文件,這些文件是在一分鐘前創建的,或者是兩天前修改的。 – GnP 2017-09-12 03:23:15

+0

這個回答是基於提出的問題。此外,您可以調整該命令以查看大約一天前發佈的最新文件。這取決於你想要做什麼。 – Naufal 2017-09-12 15:35:46

2

findls行之有效的

  • 文件名不帶換行的組合
  • 不是很大量文件的
  • 不是很長文件名

解決辦法:

find . -name "my-pattern" ... -print | 
    xargs -0 ls -1 -t | 
    head -1 

讓我們來分析一下:

隨着find我們可以匹配所有類似的有趣的文件:

find . -name "my-pattern" ... 

然後使用-print0我們可以安全地通過所有的文件名的ls這樣的:

find . -name "my-pattern" ... -print0 | xargs -0 ls -1 -t 

ls -t將文件排序通過修改時間(最新的第一個)並打印一行。您可以使用-c按創建時間進行排序。 注意:這將打破包含換行符的文件名。

終於head -1得到我們排序列表中的第一個文件。

注意:xargs使用系統限制參數列表的大小。如果這個尺寸超過,xargs將多次呼叫ls。這將打破排序,也可能是最終的輸出。運行

xargs --show-limits 

檢查系統的限制。