Bash içindeki sağlam bir yol, bir diziye genişlemek ve sadece ilk elemanı çıkarmaktır:
pattern="*.txt"
files=( $pattern )
echo "${files[0]}" # printf is safer!
(Sadece echo $files
eksik bir dizini [0] olarak kabul edebilirsiniz.)
Bu, dosya adlarını genişletirken, space / tab / newline ve diğer meta karakterleri güvenli bir şekilde işler. Geçerli yerel ayarların "ilk" in ne olduğunu değiştirebileceğini unutmayın.
Bunu bir bash tamamlama işleviyle etkileşimli olarak da yapabilirsiniz :
_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
Bu, _echo
argümanları echo
komuta tamamlama işlevini bağlar (normal tamamlamayı geçersiz kılma). Yukarıdaki koda ek bir "*" eklenir, kısmi bir dosyada sekmeye basabilirsiniz ve umarım doğru olanı ortaya çıkar.
Kod, küme veya varsayım yerine hafifçe kıvrımlıdır nullglob
( shopt -s nullglob
) compgen -G
, glob'u bazı eşleşmelere genişletebileceğini kontrol ederiz , daha sonra güvenli bir şekilde bir diziye genişleriz ve son olarak COMPREPLY'yi belirleyerek, alıntılamanın sağlam olmasını sağlarız.
Şunları yapabilirsiniz kısmen bu bash en ile (programlı bir topak genişletmek) yapmak compgen -G
, ancak Stdout'a tırnaksız çıkışı gibi sağlam değil.
Her zamanki gibi, tamamlama, ortam değişkenleri (bkz dahil diğer şeyler, bu tatili tamamlanması oldukça doludur _bash_def_completion()
işlevi burada varsayılan davranışı taklit ayrıntıları için).
Ayrıca compgen
bir tamamlama işlevinin dışında da kullanabilirsiniz :
files=( $(compgen -W "$pattern") )
Unutulmaması gereken noktalardan biri, "~" nin bir küre olmadığı, $ değişkenleri ve diğer açılımlar gibi ayrı bir genişleme aşamasında bash tarafından ele alınmasıdır. compgen -G
sadece dosya ismini globbing yapıyor, ancak compgen -W
muhtemelen çok fazla genişleme ( ``
ve de dahil olmak üzere $()
) olmak üzere bash'ın varsayılan genişlemesini veriyor . Aksine -G
, -W
bir güvenle alıntılanan (ı eşitsizlik izah edemez). Amacı -W
belirteçleri genişletmek olduğundan, bu böyle bir dosya olmasa bile "a" dan "a" ya çıkaracağı anlamına gelir, bu yüzden belki de ideal değildir.
Bunu anlamak daha kolaydır, ancak istenmeyen yan etkileri olabilir:
_echo() {
local cur=${COMP_WORDS[COMP_CWORD]}
local files=( $(compgen -W "$cur") )
printf -v COMPREPLY %q "${files[0]}"
}
Sonra:
touch $'curious \n filename'
echo curious*
tab
printf %q
Değerleri güvenle alıntı yapmak için kullanıldığına dikkat edin.
Son seçeneklerden biri GNU yardımcı programları ile 0 ayrılmış çıktı kullanmaktır ( bash SSS'ye bakın ):
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 )
Bu seçenek, sıralama düzeni üzerinde size biraz daha fazla kontrol sağlar (bir küreyi genişletme sırası yerelinize göre olacaktır / LC_COLLATE
ve büyük / küçük harf olabilir veya olmayabilir), ancak aksi takdirde böyle küçük bir sorun için oldukça büyük bir çekiç ;-)