Basit cevap şudur: tüm sınırlayıcıları birine (ilkine) daraltın.
Bu bir döngü gerektirir (bu da log(N)
zamandan az çalışır )
var=':a bc::d ef:#$%_+$$% ^%&*(*&*^
$#,.::ghi::*::' # a long test string.
d=':@!#$%^&*()_+,.' # delimiter set
f=${d:0:1} # first delimiter
v=${var//["$d"]/"$f"}; # convert all delimiters to
: # the first of the delimiter set.
tmp=$v # temporal variable (v).
while
tmp=${tmp//["$f"]["$f"]/"$f"}; # collapse each two delimiters to one
[[ "$tmp" != "$v" ]]; # If there was a change
do
v=$tmp; # actualize the value of the string.
done
Tek yapmanız gereken dizeyi bir sınırlayıcıya doğru şekilde bölmek ve yazdırmaktır:
readarray -td "$f" arr < <(printf '%s%s' "$v"'' "$f")
printf '<%s>' "${arr[@]}" ; echo
set -f
IFS değiştirmeye veya değiştirmeye gerek yok .
Boşluklar, yeni satırlar ve glob karakterlerle test edildi. Bütün iş. Oldukça yavaş (bir kabuk döngüsünün olması gerektiği gibi).
Ama sadece bash için ( -d
yeniden basma seçeneği nedeniyle bash 4.4+ ).
sh
Kabuk sürümü bir dizi kullanamaz, kullanılabilir tek dizi konum parametreleridir.
Kullanmak tr -s
sadece bir satırdır (IFS kodda değişmez):
set -f; IFS=$f command eval set -- '$(echo "$var" | tr -s "$d" "[$f*]" )""'
Ve yazdırın:
printf '<%s>' "$@" ; echo
Hala yavaş, ama çok fazla değil.
command
Bourne'de komut geçersiz.
Zsh'de, command
yalnızca harici komutları çağırır ve command
kullanılırsa eval başarısız olur .
Ksh command
cinsinden bile , IFS değeri küresel kapsamda değiştirilir.
Ve command
mksh ile ilgili mermilerde (mksh, lksh, posh) ayrılmayı başarısız kılıyor Komutun kaldırılması command
, kodun daha fazla mermi üzerinde çalışmasını sağlar. Ancak: kaldırma command
, IFS'nin bash (posix modu olmadan) ve zsh varsayılan (öykünme yok) modu hariç çoğu kabukta (eval özel bir yerleşiktir) değerini koruyacaktır. Bu kavram varsayılan zsh ile ya da onsuz çalışmak için yapılamaz command
.
Çok karakterli IFS
Evet, IFS çok karakterli olabilir, ancak her karakter bir bağımsız değişken oluşturur:
set -f; IFS="$d" command eval set -- '$(echo "$var" )""'
printf '<%s>' "$@" ; echo
Çıktı olacak:
<><a bc><><d ef><><><><><><><><>< ><><><><><><><><><
><><><><><><ghi><><><><><>
Bash ile command
sh / POSIX emülasyonunda değilse kelimeyi atlayabilirsiniz . Komut ksh93'te başarısız olur (IFS değiştirilen değeri tutar). Zsh'de komut command
, zsh'ı eval
harici bir komut olarak bulmaya çalışır (bulamaz) ve başarısız olur.
Olan şey, bir sınırlayıcıya otomatik olarak daraltılmış olan yalnızca IFS karakterlerinin IFS beyaz alanı olmasıdır.
IFS'deki bir boşluk, ardışık tüm boşlukları bire daraltır. Bir sekme tüm sekmeleri daraltır. Bir boşluk ve bir sekme boşlukları ve / veya sekmeleri bir sınırlayıcıya daraltır. Fikri newline ile tekrarlayın.
Birkaç sınırlayıcıyı daraltmak için biraz hokkabazlık yapmak gerekir.
Girişte ASCII 3 (0x03) kullanılmadığı varsayılarak var
:
var=${var// /$'\3'} # protect spaces
var=${var//["$d"]/ } # convert all delimiters to spaces
set -f; # avoid expanding globs.
IFS=" " command eval set -- '""$var""' # split on spaces.
set -- "${@//$'\3'/ }" # convert spaces back.
Ksh, zsh ve bash (about command
ve IFS) hakkındaki yorumların çoğu burada hala geçerlidir.
$'\0'
Metin girişinde değeri daha düşük olabilir, ancak bash değişkenleri NUL ( 0x00
) içeremez .
Sh'de aynı dize işlemlerini yapmak için dahili komutlar yoktur, bu nedenle tr, sh komut dosyaları için tek çözümdür.