Gerçekten yapmanız gereken, değişken içindeki tüm boş olmayan çizgileri bir şekilde (saymak da dahil olmak üzere) işlemek olan şaşırtıcı derecede sık olan durumlarda , IFS'yi sadece yeni bir satıra ayarlayabilir ve ardından kabuğun kelime ayırma mekanizmasını kırmak için kullanabilirsiniz. boş olmayan çizgiler birbirinden ayrılır.
Örneğin, sağlanan tüm bağımsız değişkenlerin içindeki boş olmayan satırları toplayan küçük bir kabuk işlevi:
lines() (
IFS='
'
set -f #disable pathname expansion
set -- $*
echo $#
)
Burada parantez yerine parantezler, işlev gövdesi için bileşik komutu oluşturmak üzere kullanılır. Bu, işlevin bir alt kabukta yürütülmesini sağlar, böylece her çağrıda dış dünyanın IFS değişkenini ve yol adı genişletme ayarını kirletmez.
Boş olmayan satırlar üzerinden yineleme yapmak istiyorsanız, bunu benzer şekilde yapabilirsiniz:
IFS='
'
set -f
for line in $lines
do
printf '[%s]\n' $line
done
IFS'yi bu şekilde değiştirmek, genellikle gözden kaçan bir tekniktir ve sekmeyle ayrılmış sütunsal girdiden boşluklar içerebilecek yol adlarını ayrıştırmak gibi şeyler yapmak için de kullanışlıdır. Bununla birlikte, IFS'nin space-tab-newline'ın varsayılan ayarında yer alan boşluk karakterini kasıtlı olarak kaldırmanın, normalde görmeyi beklediğiniz yerlerde kelime bölünmesini devre dışı bırakabileceğini bilmeniz gerekir.
Örneğin, değişkenler için karmaşık bir komut satırı oluşturmak üzere değişkenler kullanıyorsanız ffmpeg
, -vf scale=$scale
yalnızca değişken scale
boş olmayan bir şeye ayarlandığında dahil etmek isteyebilirsiniz . Normalde bunu ile başarabilirsiniz, ${scale:+-vf scale=$scale}
ancak IFS, bu parametre genişletme tamamlandığında normal boşluk karakterini içermiyorsa, arasındaki -vf
ve scale=
bir kelime ayırıcısı olarak kullanılmayacak ve ffmpeg
hepsi -vf scale=$scale
tek bir argüman olarak geçirilecektir , ki anlamayacak.
Bu sorunu gidermek için, emin IFS yapmadan önce daha normal kuruldu yapmak ya ihtiyacı olur ${scale}
genişleme ya da iki genişlemeleri yapın: ${scale:+-vf} ${scale:+scale=$scale}
. Kabuğun komut satırlarını ilk ayrıştırma sürecinde yaptığı bölme sözcüğü, bu komut satırlarını işlemenin genişletme aşamasında yaptığı bölme yerine, IFS'ye bağlı değildir.
Bu tür bir şey yapacaksanız, sadece bir sekmeyi ve sadece bir satırsonu tutmak için iki küresel kabuk değişkeni yaratmak olacaktır:
t=' '
n='
'
Bu şekilde , tüm kodunuzu tırnak işaretli boşlukla doldurmak yerine sekmelere ve yeni satırlara ihtiyacınız olan genişletmeleri dahil edebilir $t
ve ekleyebilirsiniz $n
. Bunu yapmak için başka bir mekanizmaya sahip olmayan bir POSIX kabuğunda alıntılanmış boşluktan tamamen kaçınmayı tercih ederseniz printf
, komut genişletmelerinde son satırların kaldırılması için biraz uğraşmanız gerekmesine rağmen yardımcı olabilir:
nt=$(printf '\n\t')
n=${nt%?}
t=${nt#?}
Bazen IFS'yi komut başına ortam değişkeni gibi ayarlamak iyi çalışır. Örneğin, sekmeyle ayrılmış giriş dosyasının her satırından boşluk ve ölçeklendirme faktörü içermesine izin verilen bir yol adını okuyan bir döngü:
while IFS=$t read -r path scale
do
ffmpeg -i "$path" ${scale:+-vf scale=$scale} "${path%.*}.out.mkv"
done <recode-queue.txt
Bu durumda, read
yerleşik IFS'nin sadece bir sekmeye ayarlandığını görür, böylece okuduğu giriş satırını boşluklarda da bölmez. Ama IFS=$t set -- $lines
değil işi: kabuk genişlediğinde $lines
bunun yanı inşa set
builtin en argümanlar önce kendisi yerleşik yürütülürken sadece geçerli bir şekilde IFS geçici ayarı çok geç gelir, böylece komutu çalıştırılıyor. Bu yüzden yukarıda verdiğim kod parçacıkları IFS'yi ayrı bir adımda ayarladılar ve bu nedenle koruma sorunu ile uğraşmak zorundalar.
wc -l
, orijinaline tam olarak eşdeğerdir:<<<$foo
değerine yeni bir satır ekler$foo
($foo
boş olsa bile ). Cevabımda bunun neden istenmediğini açıklıyorum, ama sorulan şey bu.