BashやZshなどのシェルは、ワイルドカードを引数に展開します。引数と同じ数の引数がパターンに一致します。
$ echo *.txt
1.txt 2.txt 3.txt
しかし、すべての一致ではなく、最初の一致のみが返されるようにするにはどうすればよいですか?
$ echo *.txt
1.txt
シェル固有のソリューションは気にしませんが、ファイル名の空白で機能するソリューションが欲しいのですが。
Bashの強力な方法の1つは、配列に展開して、最初の要素のみを出力することです。
_pattern="*.txt"
files=( $pattern )
echo "${files[0]}" # printf is safer!
_
(_echo $files
_でもかまいません。欠落しているインデックスは[0]として扱われます。)
これは、ファイル名を展開するときに、スペース/タブ/改行およびその他のメタ文字を安全に処理します。実際のロケール設定は、「最初の」とは何かを変更する可能性があることに注意してください。
Bash completion functionを使用してインタラクティブにこれを行うこともできます:
__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
コマンドへの引数を完了します(通常の完了を上書きします)。上記のコードには余分な「*」が追加されています。ファイル名の一部をTabキーで押すだけで、適切な結果が得られます。
コードはわずかに複雑であり、設定または仮定するのではなく、nullglob
(_shopt -s nullglob
_)をチェックします_compgen -G
_は、グロブをいくつかの一致に展開してから、安全に展開します配列に入れ、最後にCOMPREPLYを設定して、引用が堅牢になるようにします。
部分的に bashの_compgen -G
_でこれを(プログラムでグロブを拡張する)できますが、引用符で囲まれていない標準出力に出力するため、堅牢ではありません。
いつものように、補完はかなりうまくいきません。これは、環境変数を含む他のものの補完を壊します(デフォルトの動作のエミュレーションの詳細については、_bash_def_completion()
function here を参照してください)。
補完関数の外でcompgen
を使用することもできます。
_files=( $(compgen -W "$pattern") )
_
"〜"はグロブではなく、$ variablesやその他の展開と同様に、展開の別の段階でbashによって処理されることに注意してください。 _compgen -G
_はファイル名の展開を行うだけですが、_compgen -W
_はbashのデフォルトの展開をすべて提供しますが、展開が多すぎる可能性があります(_``
_および$()
を含む)。 _-G
_とは異なり、_-W
_ isは安全に引用されています(格差については説明できません)。 _-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
_を使用していることに注意してください。
最後のオプションの1つは、GNUユーティリティ( bash FAQ を参照)で0区切りの出力を使用することです。
_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 )
_
このオプションを使用すると、並べ替え順序を少し制御できます(グロブを展開するときの順序は、locale/_LC_COLLATE
_の影響を受け、大文字と小文字を区別する場合としない場合があります)が、それ以外の場合は、このような小さな問題;-)
Zshでは、[1]
glob qualifier 。この特殊なケースでは最大で1つの一致が返されますが、それでもリストであり、割り当て(配列の割り当ては別)などの単一のWordを期待するコンテキストではグロブは展開されません。
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"
試してください:
for i in *.txt; do printf '%s\n' "$i"; break; done
1.txt
ファイル名の展開は、現在のロケールで有効な照合順序に従ってソートされることに注意してください。
簡単な解決策:
sh -c 'echo "$1"' sh *.txt
または、必要に応じてprintf
を使用します。
同じことを思いながら、私はこの古い質問に出くわしました。私はこれで終わりました:
echo $(ls *.txt | head -n1)
もちろん、head
をtail
に、_-n1
_を他の数値に置き換えることができます。
名前に改行が含まれているファイルを操作している場合、上記は機能しません。改行を処理するには、次のいずれかを使用できます。
ls -b *.txt | head -n1 | sed -E 's/\\n/\n/g'
_ (BSDでは機能しません)ls -b *.txt | head -n1 | sed -e 's/\\n/\'$'\n/g'
_ls -b *.txt | head -n1 | Perl -pe 's/\\n/\n/g'
_echo -e "$(ls -b *.txt | head -n1)"
(任意の特殊文字で機能)