最も古い20個のファイルを1つのフォルダーから別のフォルダーに移動するスクリプトを作成するにはどうすればよいですか?フォルダ内の最も古いファイルを取得する方法はありますか?
ls
の出力の解析は 信頼できない です。
代わりに、find
を使用してファイルを検索し、sort
を使用してタイムスタンプ順に並べ替えます。例えば:
_while IFS= read -r -d $'\0' line ; do
file="${line#* }"
# do something with $file here
done < <(find . -maxdepth 1 -printf '%T@ %p\0' \
2>/dev/null | sort -z -n)
_
最初に、find
コマンドは、現在のディレクトリ(_.
_)のすべてのファイルとディレクトリを検索しますが、現在のディレクトリのサブディレクトリ(_-maxdepth 1
_)は検索しません。次に、出力します。
タイムスタンプは重要です。 _%T@
_の_-printf
_形式指定子は、ファイルの「最終変更時刻」(mtime)を示すT
と、小数秒を含む「1970年からの秒数」を示す_@
_に分類されます。 。
スペースは単なる任意の区切り文字です。ファイルへのフルパスは後で参照できるようにするためのものであり、NULL文字はファイル名に含まれる不正な文字であるためターミネーターであり、そのため、パスへのパスの最後に到達したことを確実に知らせますファイル。
_2>/dev/null
_を含めたのは、ユーザーがアクセスする権限を持っていないファイルは除外されますが、除外されているというエラーメッセージは抑制されます。
find
コマンドの結果は、現在のディレクトリ内のすべてのディレクトリのリストです。リストはsort
にパイプで渡され、次のように指示されます。
-z
_ NULLを改行ではなく行終了文字として扱います。-n
_数値で並べ替えSeconds-since-1970が常に上がるので、タイムスタンプが最も小さい番号のファイルが必要です。 sort
の最初の結果は、最も小さい番号のタイムスタンプを含む行になります。あとは、ファイル名を抽出するだけです。
find
、sort
パイプラインの結果は プロセス置換 を介してwhile
に渡され、そこでstdin上のファイルであるかのように読み取られます。次に、while
はread
を呼び出して入力を処理します。
read
のコンテキストでは、IFS
変数に何も設定しません。これは、空白が区切り文字として不適切に解釈されないことを意味します。 read
には、エスケープ拡張を無効にする_-r
_と、行末の区切り文字をNULLにする_-d $'\0'
_が通知され、find
、sort
パイプラインからの出力と一致します。
タイムスタンプとスペースが前に付いた最も古いファイルパスを表す最初のデータチャンクは、変数line
に読み込まれます。次に、 パラメーター置換 を_#*
_という式で使用します。これは、文字列の先頭から最初のスペースまでのすべての文字を、スペースを含めて何も置き換えないだけです。これにより、変更タイムスタンプが取り除かれ、ファイルへのフルパスのみが残ります。
この時点で、ファイル名は_$file
_に保存され、好きなように操作できます。 _$file
_の処理が完了すると、while
ステートメントがループし、read
コマンドが再度実行され、次のチャンクと次のファイル名が抽出されます。
いいえ。より単純な方法はバグがあります。
_ls -t
_を使用し、head
またはtail
(またはanything)にパイプすると、ファイル名に改行が含まれるファイルで中断されます。 mv $(anything)
の場合、名前に空白が含まれているファイルは破損の原因になります。 mv "$(anything)"
の場合、名前の末尾に改行が含まれるファイルは破損します。 _-d $'\0'
_なしでread
を使用すると、名前に空白が含まれているファイルが壊れます。
おそらく、特定のケースでは、より単純な方法で十分であることが確実であることがわかっていますが、そうすることを避けることができる場合は、スクリプトにそのような仮定を記述しないでください。
_#!/usr/bin/env bash
# move to the first argument
dest="$1"
# move from the second argument or .
source="${2-.}"
# move the file count in the third argument or 20
limit="${3-20}"
while IFS= read -r -d $'\0' line ; do
file="${line#* }"
echo mv "$file" "$dest"
let limit-=1
[[ $limit -le 0 ]] && break
done < <(find "$source" -maxdepth 1 -printf '%T@ %p\0' \
2>/dev/null | sort -z -n)
_
次のように呼び出します:
_move-oldest /mnt/backup/ /var/log/foo/ 20
_
最も古い20個のファイルを_/var/log/foo/
_から_/mnt/backup/
_に移動します。
ファイルおよびディレクトリを含めていることに注意してください。ファイルの場合のみ、_-type f
_をfind
呼び出しに追加します。
この回答を改善してくれた enzotib と ПавелТанков に感謝します。
ls -t
の出力とtail
またはhead
を組み合わせます。
すべてのファイル名に空白と\[*?
以外の印刷可能な文字のみが含まれている場合にのみ機能する簡単な例:
mv $(ls -1tr | head -20) other_folder
これはzshで最も簡単で、Om
glob qualifier を使用して、一致を日付(最も古いもの)と[1,20]
修飾子を使用して、最初の20件の一致のみを保持します。
mv -- *(Om[1,20]) target/
ドットファイルも含める場合は、D
修飾子を追加します。追加 .
通常のファイルのみを照合し、ディレクトリは照合しない場合。
Zshがない場合は、Perlのワンライナーを使用します(80文字未満で実行できますが、明確にするために追加費用がかかります)。
Perl -e '@files = sort {-M $b <=> -M $a} glob("*"); foreach (@files[0..1]) {rename $_, "target/$_" or die "$_: $!"}'
POSIXツールのみ、またはbashやkshでも、日付でファイルをソートするのは面倒です。 ls
を使用すると簡単にできますが、ls
の出力の解析には問題があるため、ファイル名に改行以外の印刷可能な文字のみが含まれている場合にのみ機能します。
ls -tr | head -n 20 | while IFS= read -r file; do mv -- "$file" target/; done
GNU find for this:
find -maxdepth 1 -type f -printf '%T@ %p\n' \
| sort -k1,1 -g | head -20 | sed 's/^[0-9.]\+ //' \
| xargs echo mv -t dest_dir
Findが変更時間(1970年からの秒数)と現在のディレクトリの各ファイルの名前を出力する場合、出力は最初のフィールドに従ってソートされ、最も古い20がフィルターされ、dest_dir
に移動されます。コマンドラインをテストした場合は、echo
を削除します。
ファイル名に埋め込まれた改行文字(何でも埋め込まれたもの)に対応するbashの例を(まだ)誰も投稿していません。最も古い(mdate)3つの通常ファイルを移動します
move=3
find . -maxdepth 1 -type f -name '*' \
-printf "%T@\t%p\0" |sort -znk1 | {
while IFS= read -d $'\0' -r file; do
printf "%s\0" "${file#*$'\t'}"
((--move==0)) && break
done } |xargs -0 mv -t dest
これはtest-dataスニペットです
# make test files with names containing \n, \t and " "
rm -f '('?[1-4]' |?)'
for f in $'(\n'{1..4}$' |\t)' ;do sleep .1; echo >"$f" ;done
touch -d "1970-01-01" $'(\n4 |\t)'
ls -ltr '('?[1-4]' |'?')'; echo
mkdir -p dest
これがcheck-resultsスニペットです
ls -ltr '('?[1-4]' |'?')'
ls -ltr dest/*
GNU find
を使用するのが最も簡単です。Linuxで毎日使用していますDVRは、1日以上前のビデオ監視システムから記録を削除するために使用します。
構文は次のとおりです。
find /path/to/files/* -mtime +number_of_days -exec mv {} /path/to/folder \;
find
は、実行日から24時間を1日と定義していることに注意してください。したがって、午後11時に最後に変更されたファイルは、午前1時に削除されません。
find
とcron
を組み合わせることもできるため、次のコマンドをrootとして実行することで、削除を自動的にスケジュールできます。
crontab -e << EOF
@daily /usr/bin/find /path/to/files/* -mtime +number_of_days -exec mv {} /path/to/folder \;
EOF
マニュアルページを参照すると、find
に関する詳細情報をいつでも取得できます。
man find
他の回答は私や質問の目的に合わないため、このシェルはCentOS 7でテストされています。
oldestDir=$(find /yourPath/* -maxdepth 0 -type d -printf '%T+ %p\n' | sort | head -n 1 | tr -s ' ' | cut -d ' ' -f 2)
echo "$oldestDir"
rm -rf "$oldestDir"