2

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

tr_mine AB DC 

我想要两个数组:a[0] = A、a[1] = B 和 b[0]=C b[1]=D。

我找到了一种方法,但它不起作用:

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

3 回答 3

2

没有 sed,没有 awk,所有的 bash 内部结构。

假设单词总是用空格(空格和/或制表符)分隔,
还假设单词作为参数给出,并且只为 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 and 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 元字符(|、&、;、(,)、<、>、空格、制表符)影响的正确方法。当然,空格(即使是重复的)会按照变量的定义分割单词$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]"删除最后一个尾随“换行符”。如果不需要,请引用该行。

安全提示:这里的 eval 不是什么大问题,因为它一次只处理一个字符。仅基于一个角色就很难进行攻击。无论如何,通常的警告是有效的:在使用这个脚本之前总是清理你的输入。此外,bash 的大多数(未引用的)元字符都会破坏这个脚本。

$ script.sh qwer(rttt    fgfgfg
bash: syntax error near unexpected token `('
于 2016-04-03T23:02:52.860 回答
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 中的数组是一维的。为了伪造一个二维数组,我使用索引:w单词和l字母。这将使进一步的处理变得痛苦......
  • 关联数组没有排序,因此打印时结果以随机顺序出现
  • ${!result[@]}用于代替${result[@]}. 第一个迭代键,而第二个迭代值

我知道这不完全是您所要求的,但我希望它能为您指明正确的方向

于 2016-04-03T09:11:03.837 回答
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#,其中 # 是为每个读取的单词添加的增量。

于 2016-04-03T10:00:48.793 回答