最も古いファイルの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@
指定子は、ファイルの「最後の変更時間」(mtime)を表すものと、小数秒を含む「1970年以降の秒数」を表す-printf
と区別されます。T
@
空白は任意の区切り文字にすぎません。ファイルのフルパスは後で参照できるようにするためのものであり、NULL文字はファイル名に無効な文字なので、ファイルパスの末尾に到達したことを確認できるようにする終端者です。文書。
2>/dev/null
ユーザーがアクセスできないファイルを除外するように含めましたが、そのファイルが除外されたというエラーメッセージは表示されません。
このコマンドの結果find
は、現在のディレクトリのすべてのディレクトリのリストです。リストは にパイプされ、次をsort
表します。
-z
NULL を改行文字ではなく行終端として扱います。-n
数値順に並べ替え
1970年以降の秒数は常に増加するため、タイムスタンプの数が最も少ないファイルが必要です。最初の結果sort
は、最も低い番号のタイムスタンプを含む行になります。残りはファイル名を抽出するだけです。
find
、パイプラインの結果はsort
以下を通過します。プロセスの交換while
まるで標準入力のファイルのように読み込まれるところです。入力を処理するためにwhile
順番に呼び出されますread
。
read
変数をスペースに設定した文脈では、スペースはIFS
区切り文字として不適切に解釈されないことを意味します。エスケープ拡張を無効にし、行末の区切り文字をNULLにしてパイプの出力と一致させることがread
わかります。-r
-d $'\0'
find
sort
最初のデータブロックは、タイムスタンプとスペースが先頭にある最も古いファイルパスを表し、変数として読み込まれますline
。次に、パラメータの置換式と共に使用すると、#*
文字列の先頭から最初のスペースを含むすべての文字をnullに置き換えます。これにより、修正タイムスタンプが削除され、ファイルのフルパスのみが残ります。
この時点でファイル名が保存され、$file
必要に応じて何でもできます。何かが完了すると、$file
ステートメントwhile
が繰り返され、read
コマンドが再度実行され、次のチャンクと次のファイル名が抽出されます。
もっと簡単な方法はありませんか?
いいえ。より簡単なアプローチには欠陥があります。
使用ls -t
してパイプする場合(head
またはtail
何もない)ファイル名に改行文字があるファイルでは中断されます。mv $(anything)
名前にスペースが含まれているファイルは破損する可能性があります。mv "$(anything)"
名前の後に改行文字があるファイルは破損する可能性があります。そうread
しないと、名前-d $'\0'
にスペースを含むファイルが破損します。
より簡単な方法で十分であると確信する特定の状況があるかもしれませんが、可能であれば、そのような仮定をスクリプトに書き込むべきではありません。
解決策
#!/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
ありがとう
答え2
これを達成するためにGNU findを使用することができます:
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 個がフィルタリングされ、OK コマンドをテストした場合に移動されますdest_dir
。echo
削除してください。
答え3
利用可能なzshで最も簡単です。Om
グローバル予選日付(最も古いものから)と修飾子で一致をソートして、上位[1,20]
20の一致のみを維持します。
mv -- *(Om[1,20]) target/
D
ドットファイルを含めるには、修飾子を追加してください。.
ディレクトリではなく通常のファイルのみを一致させるには、そのファイルを追加してください。
zshがなければ、Perlの1行のコードがあります(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
答え4
ファイル名に改行文字(何でも含む)を含める必要がある要件を満たすbashの例をまだ公開していないので、ここに1つを投稿します。最も古い3つの(mdate)一般ファイルを移動します。
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
これはテストデータ破片
# 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
ここにいる検査結果破片
ls -ltr '('?[1-4]' |'?')'
ls -ltr dest/*