JavaScript'te kabukta “split ()” gibi bir şey var mı?


18

split()Bir dizeyi bir diziye ayırmak için JavaScript'te kullanımı çok kolaydır .

Kabuk betiği ne olacak?

Bunu yapmak istediğimi söyle:

$ script.sh var1_var2_var3

Kullanıcı var1_var2_var3script.sh dosyasına böyle bir dize verdiğinde, betiğin içinde dizeyi aşağıdaki gibi bir diziye dönüştürür

array=( var1 var2 var3 )
for name in ${array[@]}; do
    # some code
done

1
ne shellkullanıyorsun, bashyapabilirsinIFS='_' read -a array <<< "${string}"
gwillie

perlbunu da yapabilir. "Saf" kabuk değil, ama oldukça yaygın.
Sobrique

@Sobrique "Saf" kabuğun teknik tanımının da farkında değilim ama node.js var.
emory

Ben 'muhtemelen varsayılan olarak benim linux kutusunda yüklü' çalışma eğilimindedir ve önemsiz ayrıntılar :) üzülmek yok
Sobrique

Yanıtlar:


24

Bourne / POSIX benzeri mermilerin split + glob operatörü vardır ve liste bağlamında bir parametre genişletmesi ( $var, $-...), komut ikamesi ( $(...)) veya aritmetik genişletme ( $((...))) bıraktığınızda çağrılır .

Aslında, for name in ${array[@]}yerine bunu yanlışlıkla başlattınız for name in "${array[@]}". (Aslında, bu operatörü yanlışlıkla çağırmanın birçok hatanın ve güvenlik açıklarının kaynağı olduğuna dikkat etmelisiniz ).

Bu operatör $IFSözel parametreyle (hangi karakterlerin bölüneceğini söylemek için (boşluk, sekme ve yeni satırın orada özel bir işlem aldığını unutmayın)) ve parçayı -fdevre dışı bırakma ( set -f) veya etkinleştirme ( set +f) seçeneğiyle yapılandırılır glob.

Ayrıca, Sin $IFSorijinal $IFSolarak Separator için (geldiği Bourne kabuğunda ) iken, POSIX kabuklarında, karakterlerin sınırlayıcı veya sonlandırıcı$IFS olarak görülmesi gerektiğini unutmayın (bir örnek için aşağıya bakın).

Bölmek için _:

string='var1_var2_var3'
IFS=_ # delimit on _
set -f # disable the glob part
array=($string) # invoke the split+glob operator

for i in "${array[@]}"; do # loop over the array elements.

Ayırıcı ve ayırıcı arasındaki farkı görmek için şunu deneyin:

string='var1_var2_'

Bu onu sadece var1ve var2sadece ayıracaktır (ekstra boş eleman yok).

Dolayısıyla, JavaScript'lere benzer hale getirmek için split()ek bir adıma ihtiyacınız olacak:

string='var1_var2_var3'
IFS=_ # delimit on _
set -f # disable the glob part
temp=${string}_ # add an extra delimiter
array=($temp) # invoke the split+glob operator

(boş bir bölme olacağını not $stringiçine 1 (değil 0 JavaScript sitesi gibi güvenilir) eleman split()).

Özel tedaviler sekmesini, alanı ve yeni satır alımını görmek için karşılaştırın:

IFS=' '; string=' var1  var2  '

(nereden alırsınız var1ve var2)

IFS='_'; string='_var1__var2__'

Alacağınız burada: '', var1, '', var2, ''.

Not o zshkabuk yapar değil örtülü böyle o bölünmüş + glob operatörü çağırmak içinde olmadıkça shveya kshöykünme. Orada, onu açıkça çağırmalısınız. $=stringbölünmüş parça $~stringiçin, glob kısmı için ( $=~stringher ikisi için) ve ayrıca ayırıcıyı belirtebileceğiniz bir bölme operatörü vardır:

array=(${(s:_:)string})

veya boş elemanları korumak için:

array=("${(@s:_:)string}")

Orada unutmayın siçindir bölünme değil, sınırlayan ile (aynı zamanda $IFS, bilinen bir POSIX uyumsuzluk zsh). split()Boş bir dizenin 0 (1 değil) öğesine bölünmesi JavaScript'ten farklıdır .

İle dikkate değer bir fark $IFS-splitting olmasıdır ${(s:abc:)string}üzerinde böler abcile birlikte, dize IFS=abc, bu konuda ayıracaka , bya da c.

İle zsh ve ksh93, uzay, sekme veya satır almalarını özel tedavi bunları iki katına çıkarılabilir $IFS.

Tarihi bir not olarak, Bourne kabuğu (ata veya modern POSIX kabukları) her zaman boş unsurları soydu. Ayrıca, $ @ öğesinin varsayılan olmayan değerlerle bölünmesi ve genişletilmesi ile ilgili bir dizi hata vardı.$IFS . Örneğin IFS=_; set -f; set -- $@, eşdeğer değildir IFS=_; set -f; set -- $1 $2 $3....

Normal ifadelerde bölme

Şimdi JavaScript'lere daha yakın bir şey için split() düzenli ifadelere ayrılabilen için harici yardımcı programlara güvenmeniz gerekir.

POSIX araç göğsünde, genişletilmiş düzenli ifadelere bölünebilen awkbir splitoperatör vardır (bunlar, JavaScript tarafından desteklenen Perl benzeri normal ifadelerin bir veya daha fazla alt kümesidir).

split() {
  awk -v q="'" '
    function quote(s) {
      gsub(q, q "\\" q q, s)
      return q s q
    }
    BEGIN {
      n = split(ARGV[1], a, ARGV[2])
      for (i = 1; i <= n; i++) printf " %s", quote(a[i])
      exit
    }' "$@"
}
string=a__b_+c
eval "array=($(split "$string" '[_+]+'))"

zshKabuk Perl uyumlu düzenli (onun içinde ifade desteği yerleşik vardır zsh/pcrenispeten hantal mümkün olsa da, modül), ancak bir dize bölmek için kullanıyor.


Sekme, boşluk ve satırsonu ile özel tedavilerin herhangi bir nedeni var mı?
cuonglm

1
@cuonglm, genellikle sınırlayıcı olmayan boş sınırlayıcı durumunda, boşlukları olduğunda kelimeleri bölmek istediğiniz (bölünmeye gibi $PATHüzerinde :tam tersine), genellikle boş unsurları korumak istiyor. Bourne kabuğunda, tüm karakterlerin özel muamele kshgördüğünü, sadece boş olanların (sadece boşluk, sekme ve yeni satır) özel olarak işlenmesini değiştirdiğini unutmayın.
Stéphane Chazelas

Son eklenen Bourne mermi notu beni şaşırttı. Ve tamamlamak için zsh, 2 veya daha fazla karakter içeren dize ile tedavi için not eklemelisiniz ${(s:string:)var}?
Eklenirse,

1
"$ IFS içindeki S'nin Ayırıcı için değil, Sınırlayıcı için olduğunu unutmayın" ile ne kastediyorsunuz? Ben mekaniğini anlamak ve onu yok saydığı ayırıcılar ama sondaki Siçin stantlar Ayırıcı değil, sınırlayıcı . En azından bash el kitabım böyle söylüyor.
terdon

@terdon, ayırıcı$IFS olduğu Bourne kabuğundan geliyor , ksh adı değiştirmeden davranışı değiştirdi. (Zsh veya pdksh hariç) artık sadece bölünmediğini vurgulamaktan bahsediyorum . split+glob
Stéphane Chazelas

7

Evet, kullanın IFSve olarak ayarlayın _. Ardından read -abir dizi halinde saklamak için kullanın ( -rters eğik çizgi genişletmesini kapatır). Bunun bash'a özgü olduğunu unutmayın; ksh ve zsh, biraz farklı sözdizimine sahip benzer özelliklere sahiptir ve düz sh, dizi değişkenlerine sahip değildir.

$ r="var1_var2_var3"
$ IFS='_' read -r -a array <<< "$r"
$ for name in "${array[@]}"; do echo "+ $name"; done
+ var1
+ var2
+ var3

Gönderen man bash:

okumak

-bir isim

Kelimeler, 0'dan başlayarak dizi değişkeni aname'nin sıralı dizinlerine atanır. Aname, yeni değerler atanmadan önce ayarlanmaz. Diğer ad bağımsız değişkenleri yok sayılır.

IFS

Genişletme işleminden sonra sözcük bölme ve read yerleşik komutuyla satırları kelimelere bölme için kullanılan Dahili Alan Ayırıcı. Varsayılan değer `` '' dir.

readİlk satırda durduğunu unutmayın . Bundan kaçınmak -d ''için readgeçin, ancak bu durumda, <<<operatör nedeniyle sonunda ekstra bir yeni satır olacaktır . Manuel olarak kaldırabilirsiniz:

IFS='_' read -r -d '' -a array <<< "$r"
array[$((${#array[@]}-1))]=${array[$((${#array[@]}-1))]%?}

Bu $r, yeni satır karakterleri veya ters eğik çizgiler içermediğini varsayar . Ayrıca yalnızca bashkabuğun son sürümlerinde çalışacağını unutmayın .
Stéphane Chazelas

@ StéphaneChazelas iyi bir nokta. Evet, bu bir dizenin "temel" durumudur. Geri kalanı için, herkes kapsamlı cevabınızı vermelidir. Sürümleri ile ilgili olarak bash, read -abash 4'te tanıtıldı, değil mi?
fedorqui

1
üzgünüm benim kötü, <<<sadece son zamanlarda eklendi düşündüm bashama 2.05b (2002) beri orada görünüyor. read -adaha da yaşlı. <<<gelir zshve tarafından desteklenmektedir ksh93sıra (ve mksh ve Yash) ama read -abash özgü (öyle olduğunu -Aksh93 Yash ve zsh olarak).
Stéphane Chazelas

@ StéphaneChazelas bu değişikliklerin ne zaman olduğunu bulmanın "kolay" bir yolu var mı? Ben "kolay" diyorum sürüm dosyaları içine kazmak değil, belki hepsini gösteren bir sayfa.
fedorqui

1
Bunun için değişiklik günlüklerine bakıyorum. zsh ayrıca 3.1.5'e kadar geçmişe sahip bir git deposuna sahiptir ve posta listesi değişiklikleri izlemek için de kullanılır.
Stéphane Chazelas
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.