ベース名に基づいてファイルパス名の配列をソートします。

ベース名に基づいてファイルパス名の配列をソートします。

配列に保存されているファイルパス名のリストがあるとします。

filearray=("dir1/0010.pdf" "dir2/0003.pdf" "dir3/0040.pdf" ) 

ファイル名の基本名に基づいて配列の要素を数値順に並べ替えたいと思います。

sortedfilearray=("dir2/0003.pdf" "dir1/0010.pdf" "dir3/0040.pdf") 

どうすればいいですか?

デフォルトの名前部分でのみソートできます。

basenames=()
for file in "${filearray[@]}"
do
    filename=${file##*/}
    basenames+=(${filename%.*})
done
sortedbasenamearr=($(printf '%s\n' "${basenames[@]}" | sort -n))

考えています。

  • キーがデフォルト名で値がパス名である連想配列を生成するため、パス名へのアクセスは常にデフォルト名を介して行われます。
  • basenamesに対してのみ異なる配列を作成し、sortbasenames配列に適用します。

ありがとうございます。

答え1

sortGNU coreutils は、カスタムフィールド区切り記号とキーを受け入れます。/フルパスの代わりにデフォルト名でソートするには、フィールド区切り記号に設定し、2番目のフィールドでソートします。

printf "%s\n" "${filearray[@]}" | sort -t/ -k2生産する

dir2/0003.pdf
dir1/0010.pdf
dir3/0040.pdf

答え2

oldIFS="$IFS"; IFS=$'\n'
if [[ -o noglob ]]; then
  setglob=1; set -o noglob
else
  setglob=0
fi

sorted=( $(printf '%s\n' "${filearray[@]}" |
            awk '{ print $NF, $0 }' FS='/' OFS='/' |
            sort | cut -d'/' -f2- ) )

IFS="$oldIFS"; unset oldIFS
(( setglob == 1 )) && set +o noglob
unset setglob

ファイル名の並べ替え改行文字名前にちなんで名前を付けると、sortこのステップで問題が発生します。

最初の列のベース名と残りの列の絶対パスを含む区切り/リストを作成します。awk

0003.pdf/dir2/0003.pdf
0010.pdf/dir1/0010.pdf
0040.pdf/dir3/0040.pdf

これがすぐに並べ替えが行われる操作であり、最初cut/区切られた列を削除することです。結果は新しいbash配列に変換されます。

答え3

「およびは任意のパス名」なので、単一のディレクトリ(dir1またはdir2同じ数のディレクトリ)で構成されるとは期待できません。だから私たちは最後パス名のスラッシュは、パス名の他の場所に表示されないコンテンツを指します。その文字が@データに表示されないと仮定すると、次のようにデフォルト名でソートできます。

cat pathnames | sed 's|\(.*\)/|\1@|' | sort -t@ -k+2 | sed 's|@|/|'

最初のsedコマンドは以下を置き換えます。最後各パス名に選択された区切り文字があるスラッシュ、2番目の区切り文字は変更を元に戻します。 (単純化のために、パス名は1行に1つずつ渡すことができると仮定します。シェル変数にある場合は、最初に1行に1つずつフォーマットに変換します。)

答え4

kshやzshとは異なり、bashは配列やランダムな文字列リストのソートをデフォルトでサポートしていません。 globまたはglobの出力をソートできますがalias(最後の3つはユーザーのロケールソート順序ではありませんが)、ここでは実際には機能しません。settypeset

POSIXツールボックスには、任意の文字列リストを簡単に並べ替える機能はありません。 (sortNULおよび改行以外の短い文字シーケンスのみがあり(LINE_MAXは通常PATH_MAXより短い)、ファイルパスがNullバイトシーケンス以外の文字列になるように行をソートします。0より大きい)。

したがって、文字列比較演算子をawk使用して自己整列アルゴリズムを実装したり、<でもbash(使用[[ < ]])、bash移植可能なランダムパスの場合、最も簡単な方法はおそらく以下を使用することですperl

これにより、bash4.4+次のことができます。

readarray -td '' sorted_filearray < <(perl -MFile::Basename -l0 -e '
  print for sort {basename($a) cmp basename($b)} @ARGV' -- "${filearray[@]}")

これはstrcmp()同様のコマンドを提供します。ロケールの組み合わせ(lsglobまたは出力など)に基づいてソートするには、に-Mlocale引数を追加しますperl。数値ソート(16進ではありませんが、千単位の区切り文字の代わりにsort -g同じ数字をサポートするという点でGNUに似ています)の場合は代わりに使用します(そして、コマンドのようにユーザーの小数点を尊重します)。+31.2e-5<=>cmp-Mlocalesort

コマンドパラメータの最大サイズに制限されます。これを回避するには、perl引数を渡すのではなく、ファイルのリストを標準入力に渡すことができます。

readarray -td '' sorted_filearray < <(
  printf '%s\0' "${filearray[@]}" | perl -MFile::Basename -0le '
    chomp(@files = <STDIN>);
    print for sort {basename($a) cmp basename($b)} @files')

以前のバージョンでは、代わりにループをbash使用するか、正しく引用されたパスのリストを出力します。while IFS= read -rd ''readarray -d ''perleval "array=($(perl...))"

を使用すると、zshソート順を定義できるグローバル拡張を偽にすることができます。

sorted_filearray=(/(e{'reply=($filearray)'}oe{'REPLY=$REPLY:t'}))

私たちはreply=($filearray)実際にglobを強制的に配列/の要素に拡張します(最初はちょうど)。次に、ファイル名の終わりに基づいてソート順を定義します。

strcmp()- と同様の順序の場合、ロケールをCに変更します。数値の順序付け(合計を比較するときに大きな違いを作成しないGNUに似ているsort -V(たとえば、小数点があるロケールなど))には、glob修飾子を追加します。sort -n1.41.23.n

これに加えて、oe{expression}関数を使用してソート順序を定義することもできます。たとえば、次のようになります。

by_tail() REPLY=$REPLY:t

または次の高度なバージョン:

by_numbers_in_tail() REPLY=${(j:,:)${(s:,:)${REPLY:t}//[^0-9]/,}}

(したがって、a/foo2bar3.pdf(2,3 の数字) はb/bar1foo3.pdf(1,3) の後にソートされ、c/baz2zzz10.pdf(2,10) の前にソートされます.) 次のように使用されます.

sorted_filearray=(/(e{'reply=($filearray)'}no+by_numbers_in_tail))

もちろん、これは主な用途なので、実際の球に適用することができます。たとえば、pdfデフォルトの名前/尾でソートされた任意のディレクトリにあるファイルのリストの場合:

pdfs=(**/*.pdf(N.oe+by_tail))

1 - ベースの並べ替えが許可され、短い文字列の場合、strcmp()文字列を渡す前に16進エンコーディングに変換し、並べ替え後に再変換できます。awksort

関連情報