Düzgün bir dizi zsh zsh nasıl toplanır


42

Aşağıdakilerin çıktısını my_commandbir dizi dizide gruplayacağını düşündüm :

IFS='\n' array_of_lines=$(my_command);

böylece $array_of_lines[1]çıkış ilk satır bakın olur my_command, $array_of_lines[2]vb ikinci, vb.

Ancak, yukarıdaki komutun iyi çalışması görünmüyor. Ayrıca , bir dizi satır öğelerini satır satır yazdırdığına inanıyorum ki kontrol ettiğim gibi my_commandkarakterin çıktısını da bölüştürdüm . Bunu da kontrol ettim:nprint -l $array_of_lines

echo $array_of_lines[1]
echo $array_of_lines[2]
...

İkinci denemede, eklemenin evalyardımcı olabileceğini düşündüm :

IFS='\n' array_of_lines=$(eval my_command);

ama bununla aynı sonucu aldım.

Son olarak, zsh içindeki boşlukları içeren liste öğelerinin cevabını izleyerek, IFSzsh'ye girdiyi nasıl bölen ve öğeleri bir diziye nasıl toplayacağımızı söylemek yerine, parametre genişletme bayraklarını kullanmayı da denedim.

array_of_lines=("${(@f)$(my_command)}");

Ama yine de aynı sonucu aldım (bölünme oluyor n)

Bununla, aşağıdaki soruları var:

S1. Ne "doğru" satırları dizideki bir komutun çıktısını toplama yolları?

S2. IFSYalnızca yeni satırlarda ayrılmayı nasıl belirleyebilirim ?

Q3. @fBölünmeyi belirtmek için yukarıdaki üçüncü denememdeki gibi (yani kullanarak ) parametre genişletme bayraklarını kullanırsam , zsh değeri yok sayıyor IFSmu? Neden yukarıda işe yaramadı?

Yanıtlar:


71

TL, DR:

array_of_lines=("${(@f)$(my_command)}")

İlk hata (→ Q2): IFS='\n'setleri IFSiki karaktere \ve n. Yeni IFSbir satıra ayarlamak için kullanın IFS=$'\n'.

İkinci hata: Bir dizi değerini bir değişkene ayarlamak için, sen elemanları parantez gerekir: array_of_lines=(foo bar).

Bu, boş satırları çıkarması dışında işe yarar, çünkü ardışık boşluk tek bir ayırıcı olarak sayılır:

IFS=$'\n' array_of_lines=($(my_command))

İçeri boşluk karakterini iki katına çıkararak en sonunda hariç boş satırları koruyabilirsiniz IFS:

IFS=$'\n\n' array_of_lines=($(my_command))

Boş satırları da takip etmek için komutun çıktısına bir şey eklemeniz gerekir, çünkü bu komutun kendi içinde ayrıştırma işlemi sırasında gerçekleşmez.

IFS=$'\n\n' array_of_lines=($(my_command; echo .)); unset 'array_of_lines[-1]'

(çıktısının my_commandsınırlandırılmamış bir satırda bitmediğini varsaymak ; çıkış durumunu kaybettiğinizi de unutmayın my_command)

Yukarıdaki tüm snippet'lerin IFSvarsayılan olmayan değerleriyle bırakıldığından , sonraki kodu bozabileceklerini unutmayın. IFSYerel ayarını korumak için , her şeyi IFSyerel olarak tanımladığınız bir işleve koyun (burada ayrıca komutun çıkış durumunu korumaya da dikkat edin):

collect_lines() {
  local IFS=$'\n\n' ret
  array_of_lines=($("$@"; ret=$?; echo .; exit $ret))
  ret=$?
  unset 'array_of_lines[-1]'
  return $ret
}
collect_lines my_command

Ama ben uğraşmamayı tavsiye ederim IFS; bunun yerine, fyeni satırlara bölmek için genişletme bayrağını kullanın (→ S1):

array_of_lines=("${(@f)$(my_command)}")

Veya takip eden boş satırları korumak için:

array_of_lines=("${(@f)$(my_command; echo .)}")
unset 'array_of_lines[-1]'

Değeri IFSorada önemli değil. Testlerinizde IFSyazdırmak $array_of_linesiçin üzerine yazılan bir komut kullandığınızdan şüpheleniyorum (→ S3).


7
Bu çok karmaşık! "${(@f)...}"aynı ${(f)"..."}, ancak farklı bir şekilde. (@)İçinde çift tırnak "dizi başına bir kelime ver" (f)anlamına gelir ve "bir satır içine yeni satır ile bölmek " anlamına gelir. Not: Lütfen dokümanlar ile
uçan koyun

3
Flyingsheep, ${(f)"..."}boş satır atlamak olmaz, "${(@f)...}"onları korur. Bu $argvve arasında aynı ayrım var "$argv[@]". "$@"Bir dizinin tüm öğelerini koruyan o şey, 70'li yılların sonlarında Bourne kabuğundan geliyor.
Stéphane Chazelas

4

İki sorun: ilk olarak, görünüşe göre çift tırnak da ters eğik çizgi kaçışlarını yorumlamıyor (bunun için üzgünüm :). $'...'Tırnak kullanın . Ve man zshparambir dizideki sözcükleri toplamak için parantez içine almanız gerekir. Yani bu işe yarıyor:

% touch 'a b' c d 'e f'
% IFS=$'\n' arr=($(ls)); print -l $arr
a b
c
d
e f
% print $arr[1]
a b

Q3'üne cevap veremem. Umarım asla böyle ezoterik şeyler bilmek zorunda kalmayacağım :).


-2

Tr satırını yeni satırla değiştirmek için de kullanabilirsiniz:

lines=($(mycommand | tr '\n' ' '))
select line in ("${lines[@]}"); do
  echo "$line"
  break
done

3
Ya çizgiler boşluk içeriyorsa?
don_crissti 21:16

2
Bu hiç mantıklı değil. Hem SPC hem de NL, varsayılan değerindedir $IFS. Birini diğerine çevirmek farketmez.
Stéphane Chazelas

Düzenlemelerim makul mu? Olduğu gibi çalışmasını alamadım,
John P

(zaman aşımına uğradı) Amacın ne olduğunu gerçekten anlamadan düzenlemeyi yaptığımı itiraf ediyorum, ancak çevirinin dize işlemine dayalı ayırma için iyi bir başlangıç ​​olduğunu düşünüyorum. Beklenen davranış daha fazla olmadıkça, echogirdilerin, kimin umursadığıyla ayrılan az ya da çok kelime yığınları olmadığı durumlarda, boşluklara tercüme edilemez ve onlardan ayrılmam.
John P,
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.