最新のファイルマッチングパターンを見つけるBash関数


141

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スクリプトに含める必要があります。


4
回答のヒントについては、superuser.com / questions / 294161 /…をご覧ください。並べ替えは、最新のファイルを取得するための重要なステップです
Wolfgang Fahl

回答:


229

lsこのコマンドは、パラメータがある-t時間でソートすることを。次に、最初の(最新の)をで取得できhead -1ます。

ls -t b2* | head -1

しかし注意してください:なぜlsの出力を解析すべきではないのですか?

私の個人的な意見:解析lsは、ファイル名にスペースや改行などの面白い文字が含まれている場合にのみ危険です。ファイル名に変な文字が含まれないことが保証できる場合、解析lsは非常に安全です。

多くのシステムの多くの人がさまざまな状況で実行することを意図したスクリプトを開発している場合は、解析しないことをお勧めしますls

「正しい」方法は次のとおりです。ディレクトリ内の最新(最新、最も古い、最も古い)ファイルを見つけるにはどうすればよいですか?

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

8
他の人への注意:ディレクトリに対してこれを行う場合は、次のように-dオプションをlsに追加します。 'ls -td <pattern> | 頭-1 '
ken.ganong

5
パースLSのリンクはこれをしないと言うとにおける方法をお勧めしますBashFAQ 99。私はスクリプトに含めるための防弾ではなく1行を探しているので、@ lesmanaのように安全にlsを解析し続けます。
代名詞、2014年

1
@Eponymous:あなたは壊れやすいを使用せずに1ライナーを探しているならlsprintf "%s\n" b2* | head -1あなたのためにそれを行います。
David Ongaro

2
@DavidOngaroこの質問は、ファイル名がバージョン番号であることを示していません。これは修正時間についてです。ファイル名を仮定しても、b2.10_5_2このソリューションは無効になります。
名誉ある

1
あなたのワンライナーは私に正しい答えを与えていますが、「正しい」方法は実際に私に最も古いファイルを与えています。なぜか?
NewNameStat

15

組み合わせfindとはls適しています

  • 改行なしのファイル名
  • それほど多くないファイル
  • あまり長いファイル名ではない

ソリューション:

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

分解してみましょう:

ではfind、次のようにすべての興味深いファイルを照合できます。

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

次に-print0、次のlsようにすべてのファイル名を安全に渡すことができます:

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

追加のfind検索パラメーターとパターンをここに追加できます

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

ls -t変更時刻(新しいものが最初)でファイルをソートし、1行に1つずつ出力します。-c作成時間で並べ替えることができます。:これは改行を含むファイル名で壊れます。

最後head -1に、ソートされたリストの最初のファイルを取得します。

注: xargs引数リストのサイズにはシステム制限を使用してください。このサイズを超えると、複数回xargs呼び出されlsます。これはソートを壊し、おそらく最終的な出力も壊します。走る

xargs  --show-limits

システムの制限を確認します。

注2:find . -maxdepth 1 -name "my-pattern" -print0サブフォルダーを介してファイルを検索しない場合に使用します。

注3: @starfry-で指摘されているように、によって一致するファイルがない場合、の-r引数xargsはの呼び出しを妨げてls -1 -tいますfind。提案ありがとうございます。


2
これは、lsベースのソリューションよりも優れています。これは、lsが窒息する、非常に多くのファイルがあるディレクトリで機能するためです。
Marcin Zukowski、2018

find . -name "my-pattern" ... -print0私に与えるfind: paths must precede expression: `...'
Jaakko

ああ!...「その他のパラメータ」の略です。必要なければ、省略してください。
Boris Brodski、

2
パターンに一致するファイルがない場合、これはパターンに一致しないファイルを返す可能性があることがわかりました。これは、findがxargsに何も渡さず、ファイルリストなしでlsを呼び出し、すべてのファイルで機能するために発生します。解決策は-r、標準入力で何も受信しない場合にコマンドラインを実行しないようにxargsに指示するxargsコマンドラインに追加することです。
starfry

@starfryありがとうございます!ナイスキャッチ。-r答えを追加しました。
Boris Brodski、

7

これは、必要な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ビルトインのみを使用し、改行やその他の異常な文字を含む名前のファイルを処理する必要があります。


1
nullglob_shopt=$(shopt -p nullglob)後で使用して、以前の状態に$nullglob戻すことができますnullglob
gniourf_gniourf 2014年

$(shopt -p nullglob)を使用するという@gniourf_gniourfの提案は良いものです。$()特にCygwinでは、コマンドがビルトインのみを使用している場合でも、コマンド置換(またはバックティック)の使用は避けるのが一般的です。また、コマンドが実行されるサブシェルコンテキストにより、コマンドが予期しない動作をする場合があります。また、コマンドを変数(などnullglob_shopt)に格納しないようにしています。これは、変数の値を誤って取得すると、非常に悪い事態が発生する可能性があるためです。
pjh 2014年

見落とされたときに不明瞭な失敗につながる可能性のある詳細への注意に感謝します。ありがとう!
Ron Burk

私はあなたが問題を解決するためのよりユニークな方法を選んだことを愛しています!Unix / Linuxで「スキンcat!」の方法が複数あることは確かです。これにより多くの作業が必要な場合でも、人々の概念を示す利点があります。+1してください!
プリフタン

3

異常なファイル名(有効な\n文字を含むファイルなど)は、この種の解析で大混乱を引き起こす可能性があります。Perlでこれを行う方法を次に示します。

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

それはそこで使用されるシュワルツ変換です。


1
シュワルツがあなたと一緒にいられますように!
Nathan Monteleone

この答えはうまくいくかもしれませんが、貧弱なドキュメントを考えるとそれは信用できません。
Wolfgang Fahl、

1

statファイルグロブとファイルの時間を先頭に追加して、decorate-sort-undecorateを使用できます。

$ stat -f "%m%t%N" b2* | sort -rn | head -1 | cut -f2-

いいえ。「stat: '%m%t%N'のファイルシステム情報を読み取れません:そのようなファイルまたはディレクトリはありません」
Ken Ingram

statオプションを正しく覚えていれば、これはMac / FreeBSDバージョンのの可能性があります。他のプラットフォームで同様の出力を取得するには、次のように使用できますstat -c $'%Y\t%n' b2* | sort -rn | head -n1 | cut -f2-
Jeffrey Cash

1

find ... xargs ... head ...上記の解決策が必要な人のためのダークマジック関数の呪文ですが、関数形式が使いやすいので、考える必要はありません。

#define the function
find_newest_file_matching_pattern_under_directory(){
    echo $(find $1 -name $2 -print0 | xargs -0 ls -1 -t | head -1)
}

#setup:
#mkdir /tmp/files_to_move
#cd /tmp/files_to_move
#touch file1.txt
#touch file2.txt

#invoke the function:
newest_file=$( find_newest_file_matching_pattern_under_directory /tmp/files_to_move/ bc* )
echo $newest_file

プリント:

file2.txt

これは:

指定されたパターンに一致する指定されたディレクトリ下のファイルの最も古い変更されたタイムスタンプを持つファイル名。


1

findコマンドを使用します。

Bash 4.2以降を使用-printf '%T+ %p\n'していると仮定して、ファイルのタイムスタンプ値に使用します。

find $DIR -type f -printf '%T+ %p\n' | sort -r | head -n 1 | cut -d' ' -f2

例:

find ~/Downloads -type f -printf '%T+ %p\n' | sort -r | head -n 1 | cut -d' ' -f2

より役立つスクリプトについては、https//github.com/l3x/helpersのfind-latestスクリプトをご覧ください。


スペースを含むファイル名を使用するには、change -d '' -f2,3,4,5,6,7,8,9 ...
valodzka

0

これを達成するためのはるかに効率的な方法があります。次のコマンドを検討してください。

find . -cmin 1 -name "b2*"

このコマンドは、「b2 *」でワイルドカード検索を使用して、1分前に作成された最新のファイルを検索します。過去2日間のファイルが必要な場合は、以下のコマンドを使用することをお勧めします。

find . -mtime 2 -name "b2*"

「。」現在のディレクトリを表します。お役に立てれば。


9
「最新のファイルマッチングパターン」は実際には見つかりません... 1分前に作成された、または2日前に変更されたパターンに一致するすべてのファイルを見つけます。
GnP 2017

この回答は、提起された質問に基づいていました。また、コマンドを調整して、1日ほど前に作成された最新のファイルを確認することもできます。それはあなたが何をしようとしているのかに依存します。
Naufal

「微調整」は答えではありません。これは回答として投稿するようなものです:「検索コマンドを微調整し、何をしたいかに応じて回答を見つけてください」。
ケネットセレステ

不要なコメントについてはわかりません。私の回答が立証されないように思われる場合は、例で私の回答が意味をなさない理由を適切に説明してください。それができない場合は、コメントを控えてください。
Naufal

1
ソリューションでは、最新のファイルがいつ作成されたかを知る必要があります。それは質問にはなかったので、いいえ、あなたの答えは提起された質問に基づいていません。
Bloke Down The Pub
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.