Bash,将单词拆分成字母并保存到数组中

时间:2016-04-03 08:32:35

标签: arrays bash split ifs

我正在为一个项目挣扎。我应该编写一个bash脚本,它将像tr命令一样工作。一开始我想将所有命令参数保存到分离的数组中。如果一个参数是一个单词,我希望将每个char放在分离的数组字段中,例如。

tr_mine AB DC 

我想有两个数组:a [0] = A,a [1] = B和b [0] = C b [1] = D.

我找到了一种方法,但它无效:

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

3 个答案:

答案 0 :(得分:2)

没有sed,没有awk,所有bash internals。

假设单词总是用空格(空格和/或制表符)分隔,
还假设单词作为参数给出,并仅为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 <<<"$@"

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>

该脚本正在设置两个(数组)变量A1A2 每个字母是一个数组元素:A1 [0] = A,A1 [1] = B和A2 [0] = C,A2 [1] = D.

您需要将变量($k)设置为要访问的数组元素 例如,您需要做echo第二个字(基于1)的第四个字母(基于0)(如果需要可以更改):

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

该脚本将按以下方式工作:

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

警告:即使引用,也会拆分字符。但是,引用的参数是使用此脚本来避免shell元字符(|,&amp;,;,(,),&lt;,&gt;,space,tab)的影响的正确方法。当然,空格(即使重复)会分割由变量$blank定义的单词:

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

由于脚本将接受并正确处理已嵌入的换行符,我们需要使用:unset "${varname}${n}[$last-1]"删除最后一个跟踪&#34;换行符&#34;。如果不需要,请引用该行。

安全提示:此处的评估不是问题,因为它一次只处理一个字符。仅基于一个角色创建攻击是很困难的。无论如何,通常的警告是有效的:在使用此脚本之前始终清理您的输入。此外,bash的大多数(未引用的)元字符都会破坏此脚本。

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

答案 1 :(得分: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

说明:

  • bash不支持动态变量(即使用变量创建变量)所以我使用的是关联数组(result
  • bash中的数组是单维的。为了伪造2D数组,我使用索引:w表示单词,l表示字母。这将使进一步处理变得痛苦......
  • 关联数组未排序,因此打印时结果以随机顺序显示
  • ${!result[@]}代替${result[@]}。第一个迭代键,第二个迭代值

我知道这不是你要求的,但我希望它会指出你正确的方向

答案 2 :(得分:0)

试试这个:

sentence="$@"
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#,其中#是为每个单词读取添加的增量。