Разбить одну строку в массив символов, используя только bash


9

Я хочу разбить 'hello'на h e l l oмассив, используя только bash, я мог бы сделать это в sed с помощью, sed 's/./& /g'но я хочу знать, как разбить строку на массив в Bash, когда я не знаю, что будет разделитель, или разделитель любой один символ Я не думаю, что смогу использовать ${i// /}без какой-либо креативности, потому что разделитель неизвестен, и я не думаю, что выражение принимает регулярное выражение. Я пытался использовать BASH_REMATCH с [[string = ~ ([az].). *]], Но он не работает, как я ожидал. Как правильно использовать только bash для выполнения определенного string.split()типа поведения? Причина в том, что я пытаюсь написать утилиту rev во всех bash:

  while read data; do
  word=($(echo $data|tr ' ' '_'|sed 's/./& /g'))
  new=()
  i=$((${#word[@]} - 1))
  while [[ $i -ge 0 ]]; do
    new+=(${word[$i]})
    (( i-- ))
  done
  echo ${new[@]}|tr -d ' '|tr '_' ' '
  done

Но я использовал tr и sed, я хочу знать, как правильно делать разбиение, и тогда я исправлю это, чтобы все было bash. Просто для удовольствия.


В переполнении стека должны быть дубликаты между сайтами, учитывая их размер. Одним из кандидатов является Bash: разбить строку на массив символов .
Питер Мортенсен

[[ $string =~ ${string//?/(.)} ]]будет установлено по BASH_REMATCH[]мере необходимости, см. мой ответ на вопрос, на который Питер Мортенсен ссылается для объяснения.
mr.spuratic

Ответы:


12
s="hello"
declare -a a   # define array a
for ((i=0; i<${#s}; i++)); do a[$i]="${s:$i:1}"; done
declare -p a   # print array a in a reusable form

Вывод:

Declare -aa = '([0] = "h" [1] = "e" [2] = "l" [3] = "l" [4] = "o")'

или (пожалуйста, обратите внимание на комментарии)

s="hello"
while read -n 1 c; do a+=($c); done  <<< "$s"
declare -p a

Вывод:

Declare -aa = '([0] = "h" [1] = "e" [2] = "l" [3] = "l" [4] = "o")'

1
Второй вариант хорош, но он работает (в данном случае) только потому, что вы не заключаете в кавычки «$ c», поэтому он удаляет пробелы и расширяет звезды в $ s.
Ричи

2
Второй вариант должен быть: while read -N 1 c; do a+=("$c"); done <<< "$s". -NПозволяет читать даже переводы строк, и кавычки ("$c")избежать расширения имени пути (и некоторые другие вопросы).

1
Конечно, если последний символ новой строки должен быть исправлен, измените строку , данное: while read -N 1 c; do a+=("$c"); done <<< "$s"xи затем удалить последний символ новой строки и х добавляемые: unset a[${#a[@]}-1]; unset a[${#a[@]}-1].

4

Чтобы разбить строку на массив символов с нулевым разделителем, вы можете:

str='hello'
arr=()
i=0
while [ "$i" -lt "${#str}" ]; do
  arr+=("${str:$i:1}")
  i=$((i+1))
done

printf '%s\n' "${arr[@]}"

С разделителем, отличным от нуля, вы можете:

set -f
str='1,2,3,4,5'
IFS=',' arr=($str)
printf '%s\n' "${arr[@]}"

Если IFSможно установить, то вы можете просто сделать: IFS=',' arr=($str).
Муру

2
@muru: В этом случае вы застрянете с glob. Обновлено с отключением glob.
cuonglm

@BinaryZebra: Ах да, моя плохая, недостающая цитата. Исправлено, спасибо.
Cuonglm

2

Просто для прикола (и других снарядов) другой вариант:

word=hello
unset letter
while [ ${#word} -gt 0 ]
do
    rest=${word#?}
    letter[${#letter[*]}]=${word%$rest}
    word=$rest
done

И проверить

for l in "${!letter[@]}"
do
    echo "letter [$l] = ${letter[l]}"
done

распечатает

letter [0] = h
letter [1] = e
letter [2] = l
letter [3] = l
letter [4] = o

0

Способ 1:

Один лайнер:

s="hello"
for ((i=0;i<${#s};i++)); do result[$i]="${s:i:1}"; done
echo ${result[@]}

Расширенный код:

s="hello"                   # Original string.

for ((i=0;i<${#s};i++)); do # For i=0; i<(length of s); i++
    result[$i]="${s:i:1}"       # result[i] is equal to the i th character of $s
done                        # End of the loop

echo ${result[@]} # Print all elements of $result.

Способ 2:

Один лайнер:

s="hello"
var=($(while read -n 1; do printf "$REPLY "; done <<< "$s"))
echo "${var[@]}"

Расширенный код:

s="hello" # Original string.

while read -n 1; do # Read chraracter by character the string.
    space_separated=$(printf "$space_separated $REPLY") # $space_separated is equal to it plus the current character.
done <<< "$s" # Pass the string to the loop

result=($space_separated) # Split $space_separated into an array.

echo "${result[@]}" # Print all elements of $result.

Благодаря @cuonglm по его предложению.
По сути, вы можете использовать $REPLYпеременную по умолчанию, где readчитает входные данные.


1
Вы можете использовать $REPLYвместо charпеременной.
cuonglm
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.