web-dev-qa-db-ja.com

ディレクトリ内の最後のN個のファイルを取得するにはどうすればよいですか?

ディレクトリにファイル名で並べられたファイルがたくさんあります。最後のN(たとえば、N = 4)ファイルをホームディレクトリにコピーします。どうすればよいですか?

cp ./<the final 4 files> ~/

15
Sibbs Gambling

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

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

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

使い方

  • a=(*)

    これにより、すべてのファイル名を含む配列aが作成されます。 bashによって返されるファイル名はアルファベット順にソートされます。 (これは"ファイル名順"の意味だと思います)

  • _${a[@]: -4}_

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

  • _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コマンドを実行すると、現在のロケールを確認できます。

26
John1024

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

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

ここでOnはファイル名をアルファベット順で逆順に並べ替え、[1,4]はそれらのうち最初の4つだけを取ります。

.を使用してプレーンファイル(ディレクトリ、パイプなどを除く)のみを選択し、--cpコマンドに追加して、 -で正しく始まるので、

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

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

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

これは、非常に単純な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

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

7
gswong

シェルの並べ替えが適切である限り、次のことを実行できます。

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

これは、提供されているbash固有の配列ソリューションと非常に似ていますが、POSIX互換のシェルに移植できる必要があります。両方の方法に関する注意事項:

  1. _cp --_を使用してcp引数を指定するか、各名前の先頭に_._ドットまたは_/_のいずれかを取得することが重要です。これを実行しないと、cpの最初の引数で先頭の_-_ダッシュがリスクになり、オプションとして解釈され、操作が失敗するか、意図しない結果がレンダリングされる可能性があります。

    • 現在のディレクトリをソースディレクトリとして使用している場合でも、これは... _set -- ./*_またはarray=(./*)のように簡単に実行できます。
  2. 両方のメソッドを使用するときは、少なくともarg配列に削除しようとする数と同じ数の項目があることを確認することが重要です。ここでは、数学の拡張を使用しています。 shiftは、arg配列に少なくとも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スタイルの置換が適用されます。

6
mikeserv

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

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

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

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