我正在为一个项目而苦苦挣扎。我应该编写一个像 tr 命令一样工作的 bash 脚本。一开始我想将所有命令参数保存到单独的数组中。如果一个参数是一个单词,我希望每个字符都在分隔的数组字段中,例如。
tr_mine AB DC
我想要两个数组:a[0] = A、a[1] = B 和 b[0]=C b[1]=D。
我找到了一种方法,但它不起作用:
IFS="" read -r -a array <<< "$a"
没有 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>
该脚本正在设置两个(数组)变量A1
和A2
.
每个字母是一个数组元素: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 `('
如果可能的话,我强烈建议用另一种语言来做这件事,这会容易得多。
现在,我想出的最接近的是:
#!/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
)w
单词和l
字母。这将使进一步的处理变得痛苦......${!result[@]}
用于代替${result[@]}
. 第一个迭代键,而第二个迭代值我知道这不完全是您所要求的,但我希望它能为您指明正确的方向
尝试这个 :
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#
,其中 # 是为每个读取的单词添加的增量。