配列に保存されているファイルパス名のリストがあるとします。
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に対してのみ異なる配列を作成し、
sort
basenames配列に適用します。
ありがとうございます。
答え1
sort
GNU 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つはユーザーのロケールソート順序ではありませんが)、ここでは実際には機能しません。set
typeset
POSIXツールボックスには、任意の文字列リストを簡単に並べ替える機能はありません。 (sort
NULおよび改行以外の短い文字シーケンスのみがあり(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()
同様のコマンドを提供します。ロケールの組み合わせ(ls
globまたは出力など)に基づいてソートするには、に-Mlocale
引数を追加しますperl
。数値ソート(16進ではありませんが、千単位の区切り文字の代わりにsort -g
同じ数字をサポートするという点でGNUに似ています)の場合は代わりに使用します(そして、コマンドのようにユーザーの小数点を尊重します)。+3
1.2e-5
<=>
cmp
-Mlocale
sort
コマンドパラメータの最大サイズに制限されます。これを回避するには、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 ''
perl
eval "array=($(perl...))"
を使用すると、zsh
ソート順を定義できるグローバル拡張を偽にすることができます。
sorted_filearray=(/(e{'reply=($filearray)'}oe{'REPLY=$REPLY:t'}))
私たちはreply=($filearray)
実際にglobを強制的に配列/
の要素に拡張します(最初はちょうど)。次に、ファイル名の終わりに基づいてソート順を定義します。
strcmp()
- と同様の順序の場合、ロケールをCに変更します。数値の順序付け(合計を比較するときに大きな違いを作成しないGNUに似ているsort -V
(たとえば、小数点があるロケールなど))には、glob修飾子を追加します。sort -n
1.4
1.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進エンコーディングに変換し、並べ替え後に再変換できます。awk
sort