Dziwię się, że nikt nie wspomniał o oczywistym bash
rozwiązaniu wykorzystującym tylko while
i read
.
while read -n1 character; do
echo "$character"
done < <(echo -n "$words")
Zwróć uwagę na użycie, echo -n
aby uniknąć dodatkowego znaku nowej linii na końcu. printf
to kolejna dobra opcja, która może być bardziej odpowiednia dla Twoich szczególnych potrzeb. Jeśli chcesz ignorować spacje następnie zastąpić "$words"
z "${words// /}"
.
Inną opcją jest fold
. Należy jednak pamiętać, że nigdy nie należy go wprowadzać do pętli for. Zamiast tego użyj pętli while w następujący sposób:
while read char; do
echo "$char"
done < <(fold -w1 <<<"$words")
Główną korzyścią wynikającą z używania fold
polecenia zewnętrznego (z pakietu coreutils ) byłaby zwięzłość. Możesz przekazać jego dane wyjściowe do innego polecenia, takiego jak xargs
(część pakietu findutils ) w następujący sposób:
fold -w1 <<<"$words" | xargs -I% -- echo %
Będziesz chciał zastąpić echo
polecenie użyte w powyższym przykładzie poleceniem, które chcesz wykonać przeciwko każdemu znakowi. Zauważ, że xargs
domyślnie odrzuca białe znaki. Możesz użyć, -d '\n'
aby wyłączyć to zachowanie.
Umiędzynarodowienie
Właśnie przetestowałem fold
niektóre znaki azjatyckie i zdałem sobie sprawę, że nie ma obsługi Unicode. Więc chociaż jest to dobre dla potrzeb ASCII, nie będzie działać dla wszystkich. W takim przypadku istnieje kilka alternatyw.
Prawdopodobnie zamieniłbym fold -w1
na tablicę awk:
awk 'BEGIN{FS=""} {for (i=1;i<=NF;i++) print $i}'
Lub grep
polecenie wymienione w innej odpowiedzi:
grep -o .
Wydajność
Do Twojej wiadomości, porównałem 3 wyżej wymienione opcje. Pierwsze dwa były szybkie, prawie zawiązywane, a pętla zagięcia była nieco szybsza niż pętla while. Nic dziwnego, że xargs
był najwolniejszy ... 75x wolniejszy.
Oto (skrócony) kod testu:
words=$(python -c 'from string import ascii_letters as l; print(l * 100)')
testrunner(){
for test in test_while_loop test_fold_loop test_fold_xargs test_awk_loop test_grep_loop; do
echo "$test"
(time for (( i=1; i<$((${1:-100} + 1)); i++ )); do "$test"; done >/dev/null) 2>&1 | sed '/^$/d'
echo
done
}
testrunner 100
Oto wyniki:
test_while_loop
real 0m5.821s
user 0m5.322s
sys 0m0.526s
test_fold_loop
real 0m6.051s
user 0m5.260s
sys 0m0.822s
test_fold_xargs
real 7m13.444s
user 0m24.531s
sys 6m44.704s
test_awk_loop
real 0m6.507s
user 0m5.858s
sys 0m0.788s
test_grep_loop
real 0m6.179s
user 0m5.409s
sys 0m0.921s