Jak mogę rozdzielić litery słowa, a każdą literę w osobnym wierszu?
Na przykład, biorąc pod uwagę "StackOver"
, że chciałbym zobaczyć
S
t
a
c
k
O
v
e
r
Jestem nowy w bash, więc nie mam pojęcia, od czego zacząć.
Jak mogę rozdzielić litery słowa, a każdą literę w osobnym wierszu?
Na przykład, biorąc pod uwagę "StackOver"
, że chciałbym zobaczyć
S
t
a
c
k
O
v
e
r
Jestem nowy w bash, więc nie mam pojęcia, od czego zacząć.
Odpowiedzi:
Użyłbym grep
:
$ grep -o . <<<"StackOver"
S
t
a
c
k
O
v
e
r
lub sed
:
$ sed 's/./&\n/g' <<<"StackOver"
S
t
a
c
k
O
v
e
r
A jeśli problem stanowi puste miejsce na końcu:
sed 's/\B/&\n/g' <<<"StackOver"
Wszystko to przy założeniu GNU / Linux.
Here string
, grosso modo odpowiednik po echo foo | ...
prostu mniej pisania. Zobacz tldp.org/LDP/abs/html/x17837.html
.
na \B
(nie pasuje do granicy słów).
sed
jak:sed -et -e's/./\n&/g;//D'
Jeśli chcesz drukować tekst w pionie, możesz chcieć przełamać klastry grafemów zamiast znaków. Na przykład z e
ostrym akcentem:
W przypadku klastrów grafemowych ( e
z ostrym akcentem byłby jeden klaster grafemowy):
$ perl -CLAS -le 'for (@ARGV) {print for /\X/g}' $'Ste\u301phane'
S
t
é
p
h
a
n
e
(lub grep -Po '\X'
z GNU grep zbudowanym z obsługą PCRE)
Ze znakami (tutaj z GNU grep
):
$ printf '%s\n' $'Ste\u301phane' | grep -o .
S
t
e
p
h
a
n
e
fold
ma łamać znaki, ale GNU fold
nie obsługuje znaków wielobajtowych, więc zamiast tego łamie bajty:
$ printf '%s\n' $'Ste\u301phane' | fold -w 1
S
t
e
�
�
p
h
a
n
e
Na StackOver, który składa się tylko ze znaków ASCII (więc jeden bajt na znak, jeden znak na klaster grafemu), wszystkie trzy dają ten sam wynik.
grep -Po
że nie robi tego, czego można by się spodziewać (jak grep -P
robi).
grep -Po .
znajduje znaki (a łączący ostry akcent po znaku nowej linii jest nieprawidłowy) i grep -Po '\X'
znajduje dla mnie klastry grafhem. Może być potrzebna najnowsza wersja grep i / lub PCRE, aby działała poprawnie (lub spróbuj grep -Po '(*UTF8)\X'
)
Z wieloma awk
wersjami
awk -F '' -v OFS='\n' '{$1=$1};1' <<<'StackOver'
awk -v FS='' -v OFS='\n' '{$1=$1};1'
(zastanawiając się, czy to jest bardziej mobilny, ponieważ -F ''
może uzyskując ERE: //
)
Poniżej będą ogólne:
$ awk -F '' \
'BEGIN { RS = ""; OFS = "\n"} {for (i=1;i<=NF;i++) $i = $i; print }' <file_name>
echo StackOver | sed -e 's/./&\n/g'
S
t
a
c
k
O
v
e
r
Ponieważ konkretnie poprosiłeś o odpowiedź w bash, oto sposób na zrobienie tego w czystym bashu:
while read -rn1; do echo "$REPLY" ; done <<< "StackOver"
Pamiętaj, że spowoduje to złapanie nowego wiersza na końcu „ dokumentu tutaj ”. Jeśli chcesz tego uniknąć, ale nadal iterować znaki za pomocą pętli bash, użyj, printf
aby uniknąć nowej linii.
printf StackOver | while read -rn1; do echo "$REPLY" ; done
Również Python 2 może być używany z wiersza poleceń:
python <<< "for x in 'StackOver':
print x"
lub:
echo "for x in 'StackOver':
print x" | python
lub (jak skomentował 1_CR) w Pythonie 3 :
python3 -c "print(*'StackOver',sep='\n')"
Możesz użyć fold (1)
polecenia. Jest bardziej wydajny niż grep
i sed
.
$ time grep -o . <bigfile >/dev/null
real 0m3.868s
user 0m3.784s
sys 0m0.056s
$ time fold -b1 <bigfile >/dev/null
real 0m0.555s
user 0m0.528s
sys 0m0.016s
$
Jedną znaczącą różnicą jest to, że fold odtworzy puste linie na wyjściu:
$ grep -o . <(printf "A\nB\n\nC\n\n\nD\n")
A
B
C
D
$ fold -b1 <(printf "A\nB\n\nC\n\n\nD\n")
A
B
C
D
$
Możesz obsługiwać znaki wielobajtowe, takie jak:
<input \
dd cbs=1 obs=2 conv=unblock |
sed -e:c -e '/^.*$/!N;s/\n//;tc'
Co może być bardzo przydatne, gdy pracujesz z danymi wejściowymi na żywo, ponieważ nie ma tam buforowania, a postać jest drukowana, gdy tylko będzie cała .
sed
służą skrypty. raczej nie napiszę o tym teraz - jestem dość śpiący. jest to jednak bardzo przydatne podczas czytania terminala.
dd
spowoduje to przerwanie znaków wielobajtowych, więc wynik nie będzie już tekstem, więc zachowanie sed będzie nieokreślone zgodnie z POSIX.
Możesz także używać granic słów.
$ perl -pe 's/(?<=.)(\B|\b)(?=.)/\n/g' <<< "StackOver"
S
t
a
c
k
O
v
e
r
W bash:
Działa to z dowolnym tekstem i tylko z wewnętrznymi funkcjami bash (nie jest wywoływane zewnętrzne narzędzie), więc powinno być szybkie na bardzo krótkich ciągach.
str="Stéphane áàéèëêếe"
[[ $str =~ ${str//?/(.)} ]]
(set -- "${BASH_REMATCH[@]:1}"; IFS=$'\n'; echo "$*")
Wynik:
S
t
é
p
h
a
n
e
á
à
é
è
ë
ê
ế
e
Jeśli można zmienić IFS i parametry pozycyjne, można również uniknąć wywołania podpowłoki:
str="Stéphane áàéèëêếe"
[[ $str =~ ${str//?/(.)} ]]
set -- "${BASH_REMATCH[@]:1}"
IFS=$'\n'
echo "$*"
s=stackoverflow;
$ time echo $s | fold -w1
s
t
a
c
k
o
v
e
r
real 0m0.014s
user 0m0.000s
sys 0m0.004s
aktualizacje tutaj to hacky | najszybszy | pureBashBased sposób!
$ time eval eval printf \'%s\\\\n\' \\\${s:\{0..$((${#s}-1))}:1}
s
t
a
c
k
o
v
e
r
real 0m0.001s
user 0m0.000s
sys 0m0.000s
po więcej niesamowitości
function foldh ()
{
if (($#)); then
local s="$@";
eval eval printf \'%s\\\\n\' \\\"\\\${s:\{0..$((${#s}-1))}:1}\\\";
else
while read s; do
eval eval printf \'%s\\\\n\' \\\"\\\${s:\{0..$((${#s}-1))}:1}\\\";
done;
fi
}
function foldv ()
{
if (($#)); then
local s="$@";
eval eval echo \\\"\\\${s:\{0..$((${#s}-1))}:1}\\\";
else
while read s; do
eval eval echo \\\"\\\${s:\{0..$((${#s}-1))}:1}\\\";
done;
fi
}
fold -b1
?
read -a var <<< $(echo "$yourWordhere" | grep -o "." | tr '\n' ' ')
to podzieli twoje słowo i zapisze je w tablicy var
.
for x in $(echo "$yourWordhere" | grep -o '.')
do
code to perform operation on individual character $x of your word
done