ディレクトリ内の最後のN個のファイルを取得する方法は?


15

ディレクトリ内のファイル名で並べられた多くのファイルがあります。最終的なN(たとえば、N = 4)ファイルをホームディレクトリにコピーします。どのように私はそれを行う必要がありますか?

cp ./<the final 4 files> ~/


1
以下のすべての回答では、範囲を使用してコマンドの前に「LC_ALL = ...」を追加して、範囲が適切なロケールを使用するようにする必要があります(ロケールは変更でき、文字の順序は変更できます)たとえば、一部のロケールでは、[az]にはアルファベットのすべての文字を含めることができます。ただし、文字の順序はaAbBcC .... zZです。 。)通常の選択がある:LC_ALL=C
オリヴィエ・デュラック

回答:


23

これは、bash / ksh93 / zsh配列を使用して簡単に実行できます。

a=(*)
cp -- "${a[@]: -4}" ~/

これは、スペース、タブ、改行、またはその他の難しい文字が含まれている場合でも、すべての非隠しファイル名に対して機能します(現在のディレクトリに少なくとも4つの非隠しファイルがある場合bash)。

使い方

  • a=(*)

    これにより、aすべてのファイル名を持つ配列が作成されます。bashによって返されるファイル名はアルファベット順にソートされます。(これは、「ファイル名で並べ替えられたという意味です。

  • ${a[@]: -4}

    これは、配列の最後の4つの要素を返しますa(配列にが含まれる要素が少なくとも4つある場合bash)。

  • cp -- "${a[@]: -4}" ~/

    これにより、最後の4つのファイル名がホームディレクトリにコピーされます。

同時にコピーして名前を変更するには

これにより、最後の4つのファイルがホームディレクトリにのみコピーされ、同時にa_、コピーされたファイルの名前に文字列が追加されます。

a=(*)
for fname in "${a[@]: -4}"; do cp -- "$fname" ~/a_"$fname"; done

別のディレクトリからコピーして名前を変更する

a=(./some_dir/*)代わりに使用するとa=(*)、ディレクトリがファイル名に添付されるという問題が発生します。1つの解決策は次のとおりです。

a=(./some_dir/*) 
for f in "${a[@]: -4}"; do cp "$f"  ~/a_"${f##*/}"; done

別の解決策は、サブシェルとcdサブシェル内のディレクトリを使用することです:

(cd ./some_dir && a=(*) && for f in "${a[@]: -4}"; do cp -- "$f" ~/a_"$f"; done) 

サブシェルが完了すると、シェルは元のディレクトリに戻ります。

順序が一貫していることを確認する

質問は「ファイル名で並べ替えられた」ファイルを要求します。Olivier Dulacがコメントで指摘している順序は、ロケールごとに異なります。マシンの設定に関係なく結果を修正することが重要な場合は、配列aを定義するときにロケールを明示的に指定するのが最善です。例えば:

LC_ALL=C a=(*)

localeコマンドを実行することにより、現在どのロケールにいるかを確認できます。


9

あなたが使用している場合zsh、あなたは括弧で囲むことができ()、いわゆるのリストグロブ予選たいファイルを選択します。あなたの場合、それは

cp *(On[1,4]) ~/

ここでは、On逆の順序でアルファベット順にファイル名をソートし、[1,4]最初の4それらのかかります。

を使用してプレーンファイル(ディレクトリ、パイプなどを除く)のみを選択し.、コマンドを追加--cpて名前が-適切に始まるファイルを処理することにより、これをより堅牢にすることができます。

cp -- *(.On[1,4]) ~

D隠しファイル(ドットファイル)も考慮する場合は、修飾子を追加します。

cp -- *(D.On[1,4]) ~

2
または:*([-4,-1])
ステファンシャゼル

2
@StéphaneChazelasはい、でアルファベット順にソートすることを将来の読者のために明確にすることができます通常の(方向はon)デフォルトの動作ではありませんので、これを指定する必要、と-下から数字カウントファイル名の前に。
-jimmij

7

これは、非常に単純なbashコマンドを使用したソリューションです。

find . -maxdepth 1 -type f | sort | tail -n 4 | while read -r file; do cp "$file" ~/; done

説明:

find . -maxdepth 1 -type f

現在のディレクトリ内のすべてのファイルを検索します。

sort

アルファベット順にソートします。

tail -n 4

最後の4行のみを表示します。

while read -r file; do cp "$file" ~/; done

コピーコマンドを実行する各行をループします。


6

シェルの並べ替えが同意できる限り、次のことができます。

set -- /path/to/source/dir/*
[ "$#" -le 4 ] || shift "$(($#-4))"
cp "$@" /path/to/target/dir

これはbash、提供されている-specificアレイソリューションに非常に似ていますが、POSIX互換のシェルに移植できるはずです。両方の方法に関する注意事項:

  1. cp引数をリードするcp --.、ドットまたは/各名前の先頭のいずれかを取得することが重要です。これを行わないと、オプションとして解釈される可能性のある最初の引数の先頭の-ダッシュが危険にさらさcpれ、操作が失敗したり、意図しない結果が表示される可能性があります。

    • 現在のディレクトリをソースディレクトリとして使用する場合でも、... set -- ./*やのように簡単に実行できますarray=(./*)
  2. 両方の方法を使用して、削除しようとするものと少なくとも同じ数の項目をarg配列に含めることが重要です。ここでは、数学的な拡張を行っています。shift引数配列内の少なくとも4つの項目がある場合にのみ起こる-とだけ離れ4の余剰を作るそれらの最初の引数をシフト..

    • そのため、set 1 2 3 4 5場合によってはシフトし1ますが、set 1 2 3場合によっては何もシフトしません。
    • 例:a=(1 2 3); echo "${a[@]: -4}"空白行を印刷します。

あるディレクトリから別のディレクトリにコピーする場合は、次を使用できますpax

set -- /path/to/source/dir/*
[ "$#" -le 4 ] || shift "$(($#-4))"
pax -rws '|.*/|new_prefix|' "$@" /path/to/target/dir

...これは、sedコピーするときにすべてのファイル名に-style置換を適用します。


4

ファイルのみがあり、それらの名前に空白文字や改行文字が含まれていない(変更されていない$IFS)か、グロブ文字(またはの一部の実装で印刷lsできない文字)があり、先頭が.でない場合は、これを行うことができます:

cp -- $(ls | tail -n 4) ~/

これはコマンド置換と呼ばれ、非常に便利です。tldp.org/LDP/abs/html/commandsub.html#CSPARENS
iyrin

ありがとう!できます。a_4つのファイルすべてにプレフィックスをすばやく追加する方法はありますか?試しましたecho "a_$(ls | tail -n 4)"が、最初のファイルのみを追加します。
シブスギャンブル

@SibbsGambling私のアプローチはそれに適していませんが、John1024の答えはそれに簡単に適応できます。
ハウケレイジング

5
一般に、解析ls出力は、スペースだけでなくタブ、改行、または他の有効だが「難しい」文字も含むファイル名でひどく失敗するため、悪い形式と見なされます。
HBruijn

2
@HaukeLagingたとえそれが現在問題になっていないとしても(ディレクトリに「複雑な」ファイアネームがないため)、2年後には、急に物足りなくなるかもしれません
...-glglgl

1

これは、ループコードのない単純なbashソリューションです。最後の4つのファイルを現在のディレクトリから宛先にコピーします。

$ find . -maxdepth 1 -type f |tail -n -4|xargs cp -t "$destdir"

1
find希望する順序でファイルを提供することをどのように確認しますか?名前に空白文字(改行を含む)を含むファイルが正しく処理されることをどのように確認しますか?
クサラナナンダ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.