多くのファイルを含むディレクトリがあります。
例:
aaa.txt
bbb.txt
ccc.txt
ddd.txt
ランダムな文字列(必ずしもファイル名である必要はありません)が与えられたら、その文字列の前にソートされたすべてのファイルを見つけたい(通常のアルファベット順にソート)。
例:合計をccc.txt
探したいです。bbb.txt
aaa.txt
ファイル名には通常のASCII文字のみが含まれています。LC_ALL=C
それは推測することができる。隠しファイルがありません(で始まる.
)。
潜在的な解決策は次のとおりです(作成されたテストを含む)。
$ find -isnamelessthan ccc.txt
aaa.txt
bbb.txt
これはどのように達成できますか?
答え1
そしてzsh
:
print -rC1 -- **/*(NDe['[[ $REPLY:t < ccc.txt ]]'])
どこ:
print -rC1 --
print
r
sにawおよびon1
C
olumnパラメータがあります。**/
このように、再帰検索のためにすべてのレベルのサブディレクトリ(0を含む)を一致させますfind
。(...)
一致をさらに制限するグローバル修飾子です。N
print
:一致するものがない場合にエラーが報告されず、何も印刷されないようにnullglob:D
:dotglobはと同じで、find
隠しファイルを除外しません。e['code']
:コードを実行してファイルを選択する必要があることを確認してください。ここのコードは、ail(考慮中のファイルパスのデフォルト名)の[[ $REPLY:t < ccc.txt ]]
語彙比較(memcmp()
代わりにロケール認識関数を使用)を実行しますstrcoll()
。t
$REPLY
ccc.txt
GNUシステムでは、(すべてのシェルで)次のコマンドを使用して同様の操作を実行できます。
find . -mindepth 1 -printf '%P\0' | sort -z |
LC_ALL=C gawk -v RS='\0' -F/ '$NF < "ccc.txt"'
どこ:
-mindepth 1
、スタートアップファイル(.
)を除外します。! -name .
他のスタートアップファイル²には展開されませんが、標準を使用することもできます。-printf '%P\0'
dir/aaa.txt
for のように、開始ファイルへの相対的なファイルパスを./dir/a.txt
NUL バイト (ファイルパスに現れない唯一のバイト値) で区切って印刷します。sort -z
/ globsstrcoll()
に基づいてリストをソートします。ls
LC_ALL=C
(strcoll()
sで使用されているように)を(ASCIIベースのシステムで)に変換します。awk
<
memcmp()
-v RS='\0'
入力R
レコードS
区切り文字をNULバイトに設定します(ORS
新しい行にはデフォルト値を残します)。-F/
、省略形はフィールド区切り記号-v FS=/
をに設定します。F
S
/
$NF < "ccc.txt"
:最後のフィールドを"ccc.txt"
語彙と比較し、trueの場合は、デフォルトジョブ({print}
略語{print $0}
)を実行してレコードを印刷します。
述語の場合は、-isnamelessthan
find
次のことができます(zshから)。
alias -g -- -isnamelessthan='-exec zsh -c "[[ \$1:t < \$2 ]]" zsh {}'
次のように使用されます。
find . -isnamelessthan ccc.txt ';' -print
(各ファイルを確認するために1つのインスタンスを実行するので、効率的ではありませんzsh
。)
¹ glob 自体はロケールに従ってソートされているので、次のようにします。strcoll()
find /path/to/dir ! -name dir
²内部的に呼び出されるファイルを除外するため、これはできませんが、dir
実行できますfind /path/to/dir/. ! -name .
。
答え2
ファイル名に改行文字が含まれていないとし、awk を使用します。
$ printf '%s\n' * | awk '$0 >= "ccc.txt"{exit} 1'
aaa.txt
bbb.txt
答え3
test
システムのユーティリティがある文字列が別の文字列の前にあるかどうかを判断する非標準演算子をサポートしている場合は、<
次のコマンドで使用できますfind
。
find . -exec test {} '<' ./ccc.txt \; -print
または、
find . -exec [ {} '<' ./ccc.txt ] \; -print
ここでは、現在のディレクトリに基づいてファイルのパス名を使用して比較します。他のパス名にも同じことが当てはまるからです。この<
演算子はとして引用されるか、'<'
シェルがそれをリダイレクト演算子として解釈しないようにする必要があります"<"
。\<
テストが成功すると、述語は-print
パス名を出力します。
検索を通常のファイルに制限し、サブディレクトリに移動せず、隠された名前(または考えられる他の基準)を避けるなど、追加のテストを追加します。
find . ! -path . -prune ! -name '.*' -type f -exec [ {} '<' ./ccc.txt ] \; -print
答え4
gawk
Stéphane ChazelasとEd Mortonは、awk
問題を一行でエレガントに解決するような素晴らしい答えを使用して公開しました。
しかし、将来のプログラマーはawk
これらのソリューションを詳しく理解する方法を知る必要があります。だから私の場合は、単純なforループを使用する方が良いと思います。
最高の説明があるので、Stéphaneの答えを受け入れて、ここに私の解決策も残しておきます。
target="ccc.txt"
arr=()
target="ccc.txt"
arr=()
for f in * ; do
# You can compare all string (also non-numeric) using <
# -f handles the case when there are no files present
if [[ -f $f && $f < $target ]] ; then arr+=("$f") ; fi
done
echo ${arr[@]}