web-dev-qa-db-ja.com

名前にddmmyyタイムスタンプを使用してBashでファイルを並べ替える方法

「foo.date.bar」という形式の名前の一連のファイルがあります。「date」は310715などの6桁のフィールドです。

だから例えば

foo.310715.bar
foo.260815.bar
foo.110815.bar
foo.040815.bar

スクリプトがそれらの一部を削除できるように、ファイルのメタデータではなくファイル名の日付に基づいて、これらを日付順に並べ替えたいと思います。通常、これはPythonまたはPHPで簡単に実行できますが、Bashで処理する方法を学習しようとしています。コマンドでの最初の試行

for f in $( find $dir -type f | sort -r -t. -k 2 ); do
    echo $f
done

しかし、2番目の列を数値またはアルファベット順に並べ替えても意味がないことに気付きました。日付として並べ替える必要があります。 sortに、6桁のフィールドを日付として扱う方法、または3つの2桁の列として扱う方法を伝える方法はないようです。次のステップは、sedtrなどを使用して、6桁のフィールドをsortが解析できるものに変換することでしょうか。

助けてくれてありがとう、

MB


皆さんの素晴らしい答えに感謝し、私はそれらを読むことから多くを学びました。

2
Monkeybrain

これがbash配列の乱用です。タイムスタンプを分割し、YYMMDDの順序に基づいて配列エントリを作成してから、配列を順番に出力します。

declare -a array
for file in foo.*.bar
do
  [[ $file =~ foo.([[:digit:]]{2})([[:digit:]]{2})([[:digit:]]{2}).bar ]] && \
    {
      index="${BASH_REMATCH[3]}${BASH_REMATCH[2]}${BASH_REMATCH[1]}"
      array[$index]="$file"
    }
done

for index in "${array[@]}"
do
  echo $index
done

# or
printf "%s\n" ${array[@]}
1
Jeff Schaller

次のパイプシーケンスでは、sedを使用して、最初に*.DDMMYY.*形式のファイル名を*|DD|MM|YY|*形式に変更します。再フォーマットされた出力はsortにパイプされます。ここで '|'はフィールド区切り文字として使用され、最初にYY(-k4n)、次にMM(-k3n)、最後にDD(-k2n)でソートされます。次に、ソートされた出力がパイプでsedに戻され、ファイル名が元の形式*.DDMMYY.*に変換されます。

sed 's/\.\([[:digit:]]\{2\}\)\([[:digit:]]\{2\}\)\([[:digit:]]\{2\}\)\./|\1|\2|\3|/' | \
sort -t'|' -k4n -k3n -k2n | \
sed 's/|\([[:digit:]]\{2\}\)|\([[:digit:]]\{2\}\)|\([[:digit:]]\{2\}\)|/.\1\2\3./'

次のファイルのサンプルを使用します。

$ ls *bar -1
abc.291015.bar
abc.291115.bar
abc.291215.bar
abc.301215.bar
foo.040815.bar
foo.150115.bar
foo.150914.bar
foo.260815.bar
foo.301216.bar
foo.310715.bar
xyz.010113.bar

シーケンスは次を生成します:

xyz.010113.bar
foo.150914.bar
foo.150115.bar
foo.310715.bar
foo.040815.bar
foo.260815.bar
abc.291015.bar
abc.291115.bar
abc.291215.bar
abc.301215.bar
foo.301216.bar
0
zhdason

GNUまたはFreeBSDsortがある場合は、最初にsedを使用してスワップした後、-Vまたは--version-sortオプションを使用できます。日付形式(そして、日付形式を元に戻すには、もう一度sed):

ls -1 | 
    sed -E -e 's/^(.*\.)(..)(..)(..)(.*)$/\1\4\3\2\5/' | 
    sort -V | 
    sed -E -e 's/^(.*\.)(..)(..)(..)(.*)$/\1\4\3\2\5/'

理想的には、ファイルの名前を変更して、便利な日付形式にする必要があります。例えばPerl名前変更ユーティリティの使用prename

$ prename -v 's/^(.*\.)(..)(..)(..)(.*)$/$1$4$3$2$5/' *
foo.040815.bar renamed as foo.150804.bar
foo.110815.bar renamed as foo.150811.bar
foo.260815.bar renamed as foo.150826.bar
foo.310715.bar renamed as foo.150731.bar
$ ls -1 | sort -V
foo.150731.bar
foo.150804.bar
foo.150811.bar
foo.150826.bar

(ところで、ほとんどのprename操作とは異なり、これはたまたまリバーシブルです。必要に応じて、もう一度実行して名前を元に戻すことができます)

0
cas