web-dev-qa-db-ja.com

lsによって選択されたファイルの名前を変更することはできますか?

最近ダウンロードしたファイルがいくつかあり、名前を付けたいと思います。次のようなことができます:

rename 's/ /_/g' $(ls -t | head -n5) 

実際、最後にダウンロードした5つのファイルの名前を変更したいと思います。

何か案が?

2
dmx

最初に、lsの解析は通常お勧めできません。その出力は人間が消費するためのものであり、このソリューションは一部の正当なファイル名(たとえば、改行文字を含むファイル名)では失敗します

そうは言っても、最も簡単な解決策はlsを使用しているようです(findには、変更日による並べ替えオプションがありません)。このために、パイプとxargsコマンドを使用して、最終的な名前変更を実行できます。

要するに:

ls -1t | head -n5 | xargs -I {} rename 's/ /_/g' {}

これは元のコマンドをほぼ正確に使用しますが、xargsを使用して最終的な補完を行います。 xargsは、改行で区切られたいくつかの引数を取り、それらすべてを単一のrenameコマンドに渡します。

注意事項

これは動作するはずですが、lsの解析は脆弱です。これは、改行文字を含むファイル名では壊れます。 findのような構文解析用のコマンドを使用する方がはるかに優れています。

ただし、findを使用して最終変更日で並べ替える方法がわかりません。他の誰かがこれを行う方法を知っていれば、おそらく次のようなものになるでしょう:

find . -mindepth 1 -maxdepth 1 -print0 | sort <???> -Z | head -z -n5 | xargs -0 rename 's/ /_/g'

<???>が有効な引数である場合、このコマンドチェーンは、findに、NUL文字で区切られた現在のディレクトリ内のファイルを出力し、sorthead、およびxargsはすべて改行ではなくデリミターとして受け入れます。プロセス全体の一貫性を高めます。この方法を知っていて、lsの回答をこの下の部分に置き換えたい場合は、これをコメントまたは編集してください。

2
daboross

すでに指摘したように、 解析lsは壊れやすい 。ファイル名に奇妙なタイプ(スペース、改行、制御文字など)が含まれていると壊れます。スペースを_に置き換えたいため、推奨されるアプローチは失敗します。これを行う安全な方法は次のとおりです。

stat --printf '%n/%Y\0' * | sort -rz -t'/' -k 2 | head -z -n 3 | 
    cut -d'/' -z -f 1 | xargs -0 Perl-rename 's/ /_/g'
  • stat --printf '%n/%Y\0' *:現在のディレクトリ内のすべてのファイルまたはディレクトリの名前(%n)に続いてスラッシュ(/;を使用します。ファイル名はセパレータとして安全に使用できます)、ファイルの変更時間(エポック以降の秒数(%Y)、最後にNULL文字(\0))。
  • sort -rz -k 2:これは、NULLで区切られた入力(-z)を取り、2番目の-rで区切られた値(/)(-t'/')各行のフィールド(-k 2)。
  • head -z -n 5:最初の5行(-n 5)をヌルで区切った(-z)行を保持します。
  • cut -d'/' -z -f 1:ヌル区切り(-f 1)データの最初の(/-d /で区切られた(-z)フィールドを出力します。
1
terdon

ls -qを試してみました:

IFS='
'
for file in `ls -tr1q|tail -n5`; do
  rename 's/ /_/g' "$file"
done

エラーが発生しますが、改行やその他の制御文字を含むファイル名でも、目的の名前を変更します。

Unsuccessful stat on filename containing newline at /usr/bin/rename line 59

(Perl v5.20.2を使用)

0
Philippos