2016-04-03 2 views
2

Я борюсь с проектом. Я должен написать сценарий bash, который будет работать как команда tr. Сначала я хотел бы сохранить все аргументы команд в отдельные массивы. И в случае, если аргументом является слово, я хотел бы иметь каждый символ в отдельном поле массива, например.Bash, разделить слова на буквы и сохранить в массив

tr_mine AB DC 

Я хотел бы иметь два массива: а [0] = А, а [1] = В и Ь [0] = C B [1] = D.

Я нашел способ, но он не работает:

IFS="" read -r -a array <<< "$a" 
+0

См. [Edit-help] (http://stackoverflow.com/editing-help). – Cyrus

+0

После того, как вы прочитаете слово из списка слов через 'while read -r word; do ...; done

+0

Возможно, вы должны принять один ответ: [Как работает прием ответа?] (http://meta.stackexchange.com/q/5234/300807) –

ответ

0

я настоятельно рекомендую сделать это на другом языке, если это возможно, то это будет намного проще.

Теперь, ближе я пришел вверх с:

#!/bin/bash 

sentence="AC DC" 
words=`echo "$sentence" | tr " " "\n"` 

# final array 
declare -A result 

# word count 
wc=0 

for i in $words; do 
    # letter count in the word 
    lc=0 
    for l in `echo "$i" | grep -o .`; do 
     result["w$wc-l$lc"]=$l 
     lc=$(($lc+1)) 

    done 
    wc=$(($wc+1)) 
done 

rLen=${#result[@]} 
echo "Result Length $rLen" 


for i in "${!result[@]}" 
do 
    echo "$i => ${result[$i]}" 
done 

Вышеприведенные напечатает:

Result Length 4 
w1-l1 => C 
w1-l0 => D 
w0-l0 => A 
w0-l1 => C 

Объяснение:

  • Динамические переменные не поддерживаются в Баш (т.е. создать переменные с использованием переменных), поэтому я использую вместо этого ассоциативный массив (result)
  • Массивы в bash являются одномерными. Чтобы подделать 2D-массив, я использую индексы: w для слов и l для букв. Это сделает дальнейшую обработку боли ...
  • Ассоциативных массивы не упорядочены таким образом, результаты появляются в случайном порядке при печати
  • ${!result[@]} используются вместо ${result[@]}. Первая итерация клавиша в то время как вторые перебирают значения

Я знаю, что это не совсем то, что вы просите, но я надеюсь, что он укажет на правильное направление

0

Попробуйте это:

sentence="[email protected]" 
read -r -a words <<< "$sentence" 
for word in ${words[@]}; do 
    inc=$((i++)) 
    read -r -a l${inc} <<< $(sed 's/./& /g' <<< $word) 
done 

echo ${words[1]} # print "CD" 
echo ${l1[1]} # print "D" 

Первый read читает все слова, внутренний - для букв.

Команда sed добавляет пробел после каждого письма, чтобы строка разделилась на read -a. Вы также можете использовать эту команду sed для удаления нежелательных символов из слов (например, запятых) перед расщеплением.

Если специальные символы разрешены в словах, вы можете использовать простой Grep вместо SED команды (как это было предложено в http://www.unixcl.com/2009/07/split-string-to-characters-in-bash.html):

read -r -a l${inc} <<< $(grep -o . <<< $word) 

Массив слово ${w}.

Буквы массивов называются l#, где # - приращение, добавленное для каждого прочитанного слова.

2

Нет sed, нет awk, все внутренние элементы bash.

Предполагая, что слова всегда разделяются пробелами (пробел и/или вкладки),
также при условии, что слова даются в качестве аргументов, и писать для Баш только:

#!/bin/bash 

blank=$'[ \t]' 
varname='A' 

n=1 
while IFS='' read -r -d '' -N 1 c ; do 
    if [[ $c =~ $blank ]]; then n=$((n+1)); continue; fi 
    eval ${varname}${n}'+=("'"$c"'")' 
done <<<"[email protected]" 

last=$(eval echo \${#${varname}${n}[@]})  ### Find last character index. 
unset "${varname}${n}[$last-1]"     ### Remove last (trailing) newline. 

for ((j=1;j<=$n;j++)); do 
    k="A$j[@]" 
    printf '<%s> ' "${!k}"; echo 
done 

Это установит каждый массив A1 , A2, A3 и т. Д. ... буквам каждого слова.

Значение в конце первого цикла $n - это количество обработанных слов. Печать может быть немного сложной, поэтому код доступа к каждой букве приведен выше.

Применяется к тексту образца:

$ script.sh AB DC 
<A> <B> 
<D> <C> 

Скрипт устанавливает два (массив) вары A1 и A2.
И каждая буква представляет собой один элемент массива: A1 [0] = A, A1 [1] = B и A2 [0] = C, A2 [1] = D.

Чтобы получить доступ к элементу массива, вам необходимо установить переменную ($k).
Например, для echo четвертой буквы (с 0) второго слова (1 на основе), вы должны сделать (которые могут быть изменены в случае необходимости):

k="A2[3]"; echo "${!k}"   ### Indirect addressing. 

Сценарий будет работать следующим образом:

$ script.sh ABCD efghi 
<A> <B> <C> <D> 
<e> <f> <g> <h> <i> 

Предостережение: Персонажи будут разделены, даже если они указаны. Однако приведенные аргументы - это правильный способ использования этого сценария, чтобы избежать влияния метасимволов оболочки (|, &,;, (,), <,>, пробел, вкладка). Конечно, пространства (даже если повторяется) будет разделять слова, как это определено в переменной $blank:

$ script.sh $'qwer;rttt fgf\ngfg' 
<q> <w> <e> <r> <;> <r> <t> <t> <t> 
<> 
<> 
<> 
<f> <g> <f> < 
> <g> <f> <g> 

Как сценарий будет принимать и правильно обрабатывать embebed новой строки мы должны использовать: unset "${varname}${n}[$last-1]" удалить последнюю косую «перевод строки» , Если это нежелательно, укажите строку.

безопасности Примечание: Eval не большая проблема здесь, как это только обработка один посимвольно. Было бы сложно создать атаку, основанную только на одном символе. Во всяком случае, обычное предупреждение действительно: всегда дезинфицируйте свой вход перед использованием этого скрипта. Кроме того, большинство (не цитируемых) метасимволов bash нарушают этот скрипт.

$ script.sh qwer(rttt fgfgfg 
bash: syntax error near unexpected token `(' 
+0

Почему вы не использовали 'blank = $ '\ t''? Вы действительно хотите, чтобы буквальный '' [''и''] ''был включен? –

+0

@ DavidC.Rankin Да, это диапазон значений, мне нужен символ '[]' для захвата ** одного **, если значение после '= ~' является простой переменной '$ blank'. Возможной альтернативой является отсрочка использования '[]' для фактического теста: '[[$ c = ~ [$ blank]]]'. Но я нахожу, что мне это действительно не нравится, он выглядит некорректным (даже функциональным) для меня. –

+0

@ DavidC.Ранкин У меня просто есть этот вопрос, который я не могу стереть из своего разума: что вы нашли не так с этим ответом, что вы его не повысили? Или это даже не ошибается, это не «достаточно хорошо»? Извините за беспокойство. –

Смежные вопросы