在外壳程序脚本中继续<n>不向前跳过<n>个迭代

时间:2019-05-23 13:33:11

标签: bash shell unix

我为bash中的字符串创建了一个十六进制ASCII转换器。我正在使用的应用程序将字符串中的字符([0-9],[A-Z],[a-z]以外的任何字符)更改为对应的%十六进制。例如:/更改为字符串中的%2F

我想按原样保留ASCII字符。下面是我的代码:

NAME=%2fhome%40%21%23
C_NAME=""
for (( i=0; i<${#NAME}; i++ )); do
    CHK=$(echo "{NAME:$i:1}" | grep -v "\%" &> /dev/null;echo $?)
    if [[ ${CHK} -eq 0 ]]; then
       C_NAME=`echo "$C_NAME${NAME:$i:1}"`
    else
       HEX=`echo "${NAME:$i:3}" | sed "s/%//"`
       C_NAME=`echo -n "$C_NAME";printf "\x$HEX"`
       continue 2
    fi
done
echo "$C_NAME"

输出:

 /2fhome@40!21#23

预期:

  /home@!#

因此,基本上是在进行转换,但是没有到位。它也保留了十六进制值,这告诉我continue 2语句可能无法按我期望的方式工作。请采取任何解决方法。

3 个答案:

答案 0 :(得分:2)

您只有一个循环,所以我假设您期望continue 2跳过当前循环的当前迭代和下一个迭代,但是文档help continue明确指出了

  

继续[n]
  [...]
  如果指定N,则恢复第N个封闭循环。

没有内置函数可以跳过当前循环的当前以及下一个循环,但是在您的情况下,您可以使用(( i += 2 ))代替continue 2

答案 1 :(得分:2)

使用脚本的结构进行一些简化和更正:

#!/bin/bash
name=%2fhome%40%21%23
c_name=""
for (( i=0; i<${#name}; i++ )); do
    c=${name:i:1}
    if [[ $c != % ]]; then
       c_name=$c_name$c
    else
       hex=${name:i+1:2}
       printf -v c_name "%s\x$hex" "$c_name"
       (( i += 2 ))    # stolen from Dudi Boy's answer
    fi
done
echo "$c_name"
  • 始终使用小写或大小写混合的变量,以避免名称与外壳变量或环境变量发生冲突的可能性
  • 始终使用$()代替反引号
  • 您不需要使用大多数echo命令
  • 您可以避免使用sedgrep
  • 变量绝对不能包含在printf的格式字符串中,但是在这里不能轻易避免(不过您可以改用echo -e "\x$hex"
  • 您可以在参数扩展内进行数学运算
  • %不需要在您的grep命令中转义
  • 如果直接使用$hex变量的值,则可以消除它:
    printf -v c_name "%s\x${name:i+1:2}" "$c_name"

答案 2 :(得分:1)

我真的很喜欢你的运动,并决定用awk(我目前的研究)解决它。

希望您也喜欢它。

cat script.awk

BEGIN {RS = "%[[:xdigit:]]+"} { # redefine record separtor to RegEx (gawk specific)
        decNum = strtonum("0x"substr(RT, 2)); # remove prefix # from record separator, convert hex num to dec
        outputStr = outputStr""$0""sprintf("%c", decNum); # reconstruct output string
}
END {print outputStr}

输出

echo %2fhome%40%21%23 |awk -f script.awk
/home@!#