Bash:带有表示字符的变量的 tr 命令

Bash: tr command with variables representing chars

我想使用 tr 命令将字符映射到新字符,例如:

echo "hello" | tr '[a-z]' '[b-za-b]' Will output: ifmmp

(小写字母表中的每个字母向右移动一个)

请参阅下面 '[b-za-b]' 的新字符映射:

[a b c d e f g h i j k l m n o p q r s t u v w x y z] will map to:

[b c d e f g h i j k l m n o p q r s t u v w x y z a]


但是,当我想让它旋转多次时,如何使用变量来控制 tr 命令的旋转值?

例如:移位 1:

echo "hello" | tr '[a-z]' '[b-za-b]' without variables and:

echo "hello" | tr '[a-z]' '[(a+$var)-za-(a+$var)]' where $var=1

here I have: (a+$var)-z representing the same as b-z and

....................a-(a+$var) representing the same as a-b

我曾尝试将 ascii 值转换为 char 以在 tr 命令中使用,但我认为这是不允许的。

我的问题是 bash 没有解释:

(a+$var) 作为字符 b 当 $var=1

(a+$var) 作为字符 c 当 $var=2

...等等

我如何告诉 bash 将这些等式解释为 tr 命令的字符

编辑

我尝试用数组来做,但它不起作用:

chars=( {a..z} )

var=2

echo "hello" | tr '[a-z]' '[(${chars[var]}-z)(a-${chars[var]})]'

我使用:(${chars[var]}-z) 表示 b-z 其中 var=1

因为 ${chars[1]}b 但这不起作用。我错过了什么吗?

您可以使用八进制 \XXX 字符代码来实现您想要的字符。使用八进制代码,您可以对数字进行任何算术运算,然后将它们转换为字符代码

# rotr x
#
# if 0 <= x <= 25 rotr x outputs a set specification
# that could be used as an argument to tr command
# otherwise it outputs 'a-z'

function rotr(){
   i = $(( 97 +  ))
   if [ $i -lt 97 ] ; then
        translation='a-z'
   elif [ $i -eq 97 ] ; then
        translation='1-2'  # 141 is the octal code for "a"
                                 # 172 is the octal code for "z" 
   elif [ $i -eq 98 ] ; then
        translation='2-21'
   elif [ $i -lt 122 ] ; then       # $i is between 99 and 121 ("c" and "y") 
        ii=$(echo "obase=8 ; $i" | bc)
        jj=$(echo "obase=8 ; $(( $i - 1 ))" | bc)
        translation=\$ii'-21-'\$jj
   elif [ $i -eq 122 ] ; then
        translation='21-1'
   else                              # $i > 122
        tranlation='a-z'
   fi
   echo $translation
} 

现在您可以按如下方式使用它

echo hello | tr 'a-z' $(rotr 7)

打印

olssv

您尝试做的事情无法使用 tr 完成,而 不能 处理您的要求。此外,当您打算修改和使用变量以添加到 bash 中的 glob 模式时,这是您不可能做的事情。

你可以用 bash 数组做一个巧妙的小技巧!。 tr 命令也可以在普通的 glob 模式上采用扩展数组序列。首先定义一个源数组为

source=()

现在将其内容添加为 a-z 中的字符范围列表,使用大括号扩展 as

source=({a..z})

现在对于音译数组,从源数组,通过使用索引打印数组元素按如下方式构造它

trans=()

使用技巧从最后一个语法 ${array[@]: (-num)} 获取数组元素将得到总长度 - 元素数。所以首先构建数组为

var=2
trans+=( "${source[@]:(-(26-$var))}" )

现在构建数组的第二部分,使用另一个技巧 ${array[@]:0:num} 获得第一个 num 个元素。

trans+=( "${source[@]:0:$(( $var + 1 ))}" )

所以我们现在所做的是对于给定的 var=2 值,我们将 trans 数组构建为

echo "${trans[@]}"
c d e f g h i j k l m n o p q r s t u v w x y z a b c

现在您可以在 tr 命令中轻松使用它

echo "hello" | tr "${source[*]}" "${trans[*]}"
jgnnq

您可以将其全部放入函数中并将其值打印为

transChar() {
    local source
    local trans
    local result
    source=({a..z})
    trans=()
    var=""
    input=""
    trans+=( "${source[@]:(-(26-$var))}" )
    trans+=( "${source[@]:0:$(( $var + 1 ))}" )
    result=$( echo "$input" | tr "${source[*]}" "${trans[*]}" )
    echo "$result"
}

一些测试

transChar "hello" 1
ifmmp

transChar "hello" 2
jgnnq

transChar "hello" 3
khoor

rot-random:

# generate alphabet as arr:
arr=( {1..26} ) 
i=$(($RANDOM%24+1))
# left and right
l=$(echo ${arr[$i]})
r=$(echo ${arr[$i+1]})
# reusing arr for testing:
echo ${arr[@]} | tr "a-z" "$r-za-$l"

echo "secret:"  | tr "a-z" "$r-za-$l" ; echo $l $r $i
amkzmb:
h i 7