Büyük olasılıkla boşluk içeren bir dosya adları listesiyle çalışmanın POSIX uyumlu yolu


14

Boşluk içeren dosya adları ile çalışmak için dizi kullanımını öneren Bash komut dosyası kılavuzlarını gördüm. DashAsBinSh ancak dizileri taşınabilir olmadığını önerir, bu yüzden boşluk içerebilecek dosya adları listeleri ile çalışmak için POSIX uyumlu bir yol arıyorum.

Ben aşağıdaki örnek komut dosyasını değiştirmek için arıyorum echo

foo/target/a.jar
foo/target/b.jar
bar/target/lol whitespace.jar

İşte senaryo

#!/usr/bin/env sh

INPUT="foo/target/a.jar
foo/target/b.jar
bar/target/b.jar
bar/target/lol whitespace.jar"
# this would be produced by a 'ls' command
# We can execute the ls within the script, if it helps

dostuffwith() { echo $1; };

F_LOCATIONS=$INPUT
ALL_FILES=$(for f in $F_LOCATIONS; do echo `basename $f`; done)
ALL_FILES=$(echo "$ALL_FILES" | sort | uniq)

for f in $ALL_FILES
do
    fpath=$(echo "$F_LOCATIONS" | grep -m1 $f)
    dostuffwith $fpath
done

Yanıtlar:


8

POSIX kabukları bir dizi vardır: konumsal parametreler ( $1, $2vb toplu olarak anılacaktır "$@").

set -- 'foo/target/a.jar' 'foo/target/b.jar' 'bar/target/b.jar' 'bar/target/lol whitespace.jar'
set -- "$@" '/another/one at the end.jar'

for jar do
  dostuffwith "$jar"
done

Bu elverişsizdir çünkü sadece bir tane vardır ve konumsal parametrelerin başka herhangi bir şekilde kullanımını yok eder. Konumsal parametreler, bazen bir nimet ve bazen bir lanet olan bir işlev için yereldir.

Dosya adlarınızın yeni satır içermemesi garanti edilirse, yeni satırları ayırıcı olarak kullanabilirsiniz. Değişkeni genişlettiğinizde, önce globbing'i kapatın set -fve alan bölme karakterleri listesini IFSyalnızca yeni satır içerecek şekilde ayarlayın.

INPUT="foo/target/a.jar
foo/target/b.jar
bar/target/b.jar
bar/target/lol whitespace.jar"

set -f; IFS='
'                           # turn off variable value expansion except for splitting at newlines
for jar in $INPUT; do
  set +f; unset IFS
  dostuffwith "$jar"        # restore globbing and field splitting at all whitespace
done
set +f; unset IFS           # do it again in case $INPUT was empty

Listenizdeki öğeler yeni satırlarla ayrıldığında, özellikle birçok metin işleme komutunu kullanışlı bir şekilde kullanabilirsiniz sort.

Alan ayırmanın açıkça yapılmasını istemiyorsanız (ve bunu kapatmadıysanız, globbing hariç), değişken ikamelerin her zaman çift tırnak işaretleri koymayı unutmayın.


İyi cevap ve açıklama. Bunu kabul edilmiş olarak işaretleyeceğim çünkü bu, orijinal sort | uniqadımın istendiği gibi çalışmasını sağlıyor .
Eero Aaltonen

5

Senin bu yana $INPUTayırıcı olarak değişken kullanır satırbaşıyla, ben dosyalarınızı adlarında yeni satır olmayacağını varsaymak gidiyorum. Bu nedenle, evet, dosyalar üzerinde yineleme yapmanın ve boşlukları korumanın basit bir yolu vardır.

Fikir, readkabuk yerleşimini kullanmaktır . Normalde readherhangi bir boşlukta bölünür ve böylece boşluklar onu kırar. Ama ayarlayabilirsiniz IFS=$'\n've bunun yerine sadece satırsonlarına bölünür. Böylece listenizdeki her bir satırı tekrarlayabilirsiniz.

İşte bulabileceğim en küçük çözüm:

INPUT="foo/target/a.jar
foo/target/b.jar
bar/target/b.jar
bar/target/lol whitespace.jar"

dostuffwith() {
    echo "$1"
}

echo "$INPUT" | awk -F/ '{if (!seen[$NF]++) print }' | \
while IFS=$'\n' read file; do
  dostuffwith "$file"
done

Temel awkolarak, dosya adına göre yinelenenleri yineleyen "$ INPUT" gönderir ( /son öğe daha önce görülmediyse satırı ayırır ve ardından satırı yazdırır). Awk dosya yolları listesini oluşturduktan sonra, listeyi while readyinelemek için kullanırız .


$ checkbashisms bar.sh bar.sh satırında olası bashizm 14 (<<< burada dize)
Eero Aaltonen

1
@EeroAaltonen Herestring'i kullanmamak için değiştirdi. Ancak bu değişiklik ile whiledöngü ve böylece dostuffwithbir alt kabuk yürütülür unutmayın. Böylece döngü tamamlandığında çalışan kabuğa yapılan tüm değişkenler veya değişiklikler kaybolacaktır. Tek alternatif, o kadar hoş olmayan tam bir heredoc kullanmaktır, ancak bunun tercih edileceğini düşündüm.
Patrick

Küçüklükten daha çok okunabilirliğe dayalı puanlar veriyorum. Bu kesinlikle işe yarıyor ve bunun için zaten +1.
Eero Aaltonen

IFS="\n"ters eğik çizgi ve n karakter üzerine böler. Ama içinde read file, bölünme yok. IFS="\n"yine de boş karakterleri $ IFS'den kaldırır, aksi takdirde girişin başında ve sonunda çıkarılır. Bir çizgi okumak için kanonik sözdizimi IFS= read -r lineolsa IFS=anything read -r line(sağlanan bir şey boşlukları içermez) de çalışacaktır.
Stéphane Chazelas

ayy. Bunu nasıl başardığımdan emin değilim. Sabit.
Patrick
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.