Widziałem przewodniki skryptów Bash sugerujące użycie tablicy do pracy z nazwami plików zawierającymi białe znaki. DashAsBinSh sugeruje jednak, że tablice nie są przenośne, dlatego szukam zgodnego z POSIX sposobu pracy z listami nazw plików, które mogą zawierać spacje.
Chcę zmodyfikować poniższy przykładowy skrypt, aby to zrobił echo
foo/target/a.jar
foo/target/b.jar
bar/target/lol whitespace.jar
Oto skrypt
#!/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
shell-script
filenames
quoting
posix
whitespace
Eero Aaltonen
źródło
źródło
Odpowiedzi:
Powłoki POSIX jeden układ: parametry pozycyjne (
$1
,$2
itp zbiorczo określane jako"$@"
).Jest to niewygodne, ponieważ jest tylko jeden i niszczy wszelkie inne użycie parametrów pozycyjnych. Parametry pozycyjne są lokalne dla funkcji, która czasem jest błogosławieństwem, a czasem przekleństwem.
Jeśli masz pewność, że twoje nazwy plików nie będą zawierać nowych linii, możesz użyć nowej linii jako separatora. Po rozwinięciu zmiennej najpierw wyłącz globowanie za pomocą
set -f
i ustaw listę znaków dzielących pola tak,IFS
aby zawierała tylko nową linię.Ponieważ elementy na liście są oddzielone znakami nowej linii, możesz w szczególności używać wielu poleceń przetwarzania tekstu
sort
.Pamiętaj, aby zawsze umieszczać podwójne cudzysłowy wokół podstawień zmiennych, z wyjątkiem sytuacji, gdy jawnie chcesz, aby zachodziło dzielenie pól (a także globowanie, chyba że to wyłączyłeś).
źródło
sort | uniq
krok działa zgodnie z przeznaczeniem.Ponieważ twoja
$INPUT
zmienna używa znaków nowej linii jako separatorów, zakładam, że twoje pliki nie będą miały nowych linii w nazwach. Jako taki, tak, istnieje prosty sposób na iterację plików i zachowanie białych znaków.Chodzi o to, aby użyć
read
wbudowanej powłoki. Normalnieread
dzieli się na dowolne białe spacje, więc spacje będą go łamać. Ale możesz ustawić,IFS=$'\n'
a zamiast tego podzieli się tylko na nowe linie. Możesz iterować po każdej linii na liście.Oto najmniejsze rozwiązanie, jakie mogłem wymyślić:
Zasadniczo wysyła „$ INPUT”, do
awk
którego deduplikuje się na podstawie nazwy pliku (dzieli się,/
a następnie drukuje wiersz, jeśli ostatni element nie był wcześniej widziany). Następnie, gdy awk wygeneruje listę ścieżek do plików, używamywhile read
do iteracji po liście.źródło
while
pętla, a zatemdostuffwith
jest wykonywana w podpowłoce. Tak więc wszelkie zmienne lub zmiany wprowadzone do działającej powłoki zostaną utracone po zakończeniu pętli. Jedyną alternatywą jest użycie pełnego heredoka, co nie jest takie nieprzyjemne, ale pomyślałem, że byłoby to lepsze.IFS="\n"
dzieli na odwrotny ukośnik i n znaków. Ale wread file
środku nie ma podziału.IFS="\n"
jest nadal użyteczny, ponieważ usuwa puste znaki z $ IFS, które w innym przypadku zostałyby usunięte na początku i na końcu danych wejściowych. Aby odczytać wiersz, kanoniczna składnia jestIFS= read -r line
jednakIFS=anything read -r line
(pod warunkiem , że nic nie zawiera spacji) również będzie działać.