
BashやZshなどのシェルは、パターンに一致する引数の数を使用してワイルドカードを引数に拡張します。
$ echo *.txt
1.txt 2.txt 3.txt
しかし、すべての一致ではなく最初の一致のみを返したい場合はどうすればよいですか?
$ echo *.txt
1.txt
シェル固有のソリューションは気に入らませんが、ファイル名のスペースを処理できるソリューションが必要です。
答え1
Bashで安定した方法は、配列に展開して最初の要素のみを出力することです。
pattern="*.txt"
files=( $pattern )
echo "${files[0]}" # printf is safer!
echo $files
(欠落しているインデックスを[0]として扱うこともできます。)
これは、ファイル名を拡張するときにスペース/タブ/改行やその他のメタ文字を安全に処理します。有効なロケール設定によっては、「最初」の意味が変わる場合があります。
bashを使用して対話的にこれを実行することもできます。完全な機能:
_echo() {
local cur=${COMP_WORDS[COMP_CWORD]} # string to expand
if compgen -G "$cur*" > /dev/null; then
local files=( ${cur:+$cur*} ) # don't expand empty input as *
[ ${#files} -ge 1 ] && COMPREPLY=( "${files[0]}" )
fi
}
complete -o bashdefault -F _echo echo
これは_echo
関数をecho
コマンドの完全な引数にバインドします(通常の完成をオーバーライドします)。上記のコードには追加の「*」が追加されており、部分的なファイル名でタブを押すと正しいことが起こることを願っています。
コードは少し説明することはできませんが、()を設定または想定するのではなく、nullglob
globをいくつかの一致に拡張してから安全に配列に展開し、最後に参照が信頼できるようにCOMPREPLYを設定できることを確認してください。shopt -s nullglob
compgen -G
あなたはできます部分的にbashを使用してこれを行うことはできますが(プログラミング方式でグローバルに拡張)、標準compgen -G
出力に引用符を使用せずに出力するため、強力ではありません。
いつものように、完了は非常に心配です。これは、環境変数を含む他のものの完了を中止します(_bash_def_completion()
関数を参照)。ここ基本動作シミュレーションの詳細については、参照)。
compgen
完成機能の外部でも使用できます。
files=( $(compgen -W "$pattern") )
1つの注目すべき点は、「〜」はglobではなく、$variablesや他の拡張と同様に、別の拡張ステップでbashによって処理されることです。compgen -G
ファイル名のグロービングのみを実行しますが、compgen -W
bashのデフォルトの拡張子をすべて提供します(``
および含む$()
)。-G
同じではない-W
はい安全な引用符(違いを説明できない)その目的-W
はタグを拡張することで、ファイルが存在しなくても「a」を「a」に拡張することを意味するので、おそらく理想的ではありません。
これは理解しやすいが望ましくない副作用があるかもしれません。
_echo() {
local cur=${COMP_WORDS[COMP_CWORD]}
local files=( $(compgen -W "$cur") )
printf -v COMPREPLY %q "${files[0]}"
}
それから:
touch $'curious \n filename'
echo curious*
tab
これらの値を安全に参照するために慎重に使用してくださいprintf %q
。
最後のオプションは、GNUユーティリティでゼロで区切られた出力を使用することです(参照:バッシュFAQ):
pattern="*.txt"
while IFS= read -r -d $'\0' filename; do
printf '%q' "$filename";
break;
done < <(find . -maxdepth 1 -name "$pattern" -printf "%f\0" | sort -z )
このオプションを使用すると、ソート順序をよりよく制御できますが(グローバルを拡張するときの順序はロケールによって異なり、大/小文字を折りたたむことも折りたたむLC_COLLATE
こともできます)、このような小さな問題の場合はかなり大きなハンマーです。 )
答え2
zsh では、次を使用します。[1]
グローバル予選。この特別なケースが最大1つの一致を返しても、まだリストであり、単一の単語(割り当てなど)を必要とするコンテキストではグローバル変数は拡張されません(配列割り当てを除く)。
echo *.txt([1])
ksh または bash では、一致リスト全体を配列で埋め、最初の要素を使用できます。
tmp=(*.txt)
echo "${tmp[0]}"
すべてのシェルで位置パラメータを設定し、最初のパラメータを使用できます。
set -- *.txt
echo "$1"
これにより、位置パラメータが中断されます。これを望まない場合は、サブシェルを使用できます。
echo "$(set -- *.txt; echo "$1")"
独自の位置パラメータセットを持つ関数を使用することもできます。
set_to_first () {
eval "$1=\"\$2\""
}
set_to_first f *.txt
echo "$f"
答え3
努力する:
for i in *.txt; do printf '%s\n' "$i"; break; done
1.txt
ファイル名拡張子は、現在のロケールに適用されている組み合わせの順序に従ってソートされます。
答え4
簡単な解決策:
sh -c 'echo "$1"' sh *.txt
printf
または必要に応じてそれを使用してください。