【问题标题】:Bash detect characters that don't have corresponding varBash 检测没有对应 var 的字符
【发布时间】:2015-04-15 00:04:39
【问题描述】:

我已经完成了一个脚本,它为每个字母定义了一个变量(用于加密过程),但我希望在输出中包含空格。

我有这个:

eiput_splitted="$(echo $einput | sed -e 's/\(.\)/\1\n/g')"
export eouput=""

for input in $eiput_splitted; do
    export eouput=$eouput${!input}
    //added code here
done

但我不知道如何检测循环中的空格。 我试过了

if [ $input = \  ]
if [ $input = *\ * ]
if [ $input = *"\ "* ]
if [ $input = "*\ *" ]
if [ -z ${!input+x} ] (to detect unset variables, but doesn't seem to work..)

你能帮帮我吗?

【问题讨论】:

    标签: bash variables space


    【解决方案1】:

    你可以在 BASH 中使用它:

    while IFS= read -r ch; do 
        [[ "$ch" == " " ]] && echo "space character"
    done < <(grep -o . <<< "$einput")
    
    • grep -o . &lt;&lt;&lt; "$einput"逐个字符分割输入字符串
    • &lt; &lt;(...) 称为进程替换
    • IFS= 将输入字段分隔符设置为 null(以允许 `read 读取空间)

    【讨论】:

    • 好的。我了解您的大部分代码,但不是全部。什么是“读 -r ch”? (对不起,我是初学者......)。此外,将 x
    • 是的 &lt; &lt;(...) 是相同的管道命令,但比使用管道更有效。 &lt;&lt;&lt; 在 BASH 中被称为 herestring,IFS= read -r 从输入中读取整行(这是 grep 命令的输出)。
    • 谢谢!你为我节省了很多时间!
    【解决方案2】:

    因为您不引用字符串,所以其中的任何空格都会丢失。试试这个吧。

    echo "$einput" |
    sed -e 's/\(.\)/\1\n/g' |
    while IFS= read -r input; do
        eouput="$eouput${!input}"
        :
    done
    

    根据您的进一步处理情况,我可能只在循环内使用printf '%s' "${!input}",并在必要时在done 之后在管道中处理其输出,以避免另一个变量。

    【讨论】:

    • 这大约是您的原始重构和@anubhava 的主要重构之间的一半。它避免了讨厌的临时变量,并在任何地方使用引用,并使用IFS= read -r 逐字读取每个输入标记。
    【解决方案3】:

    你的设计不是很好。你不应该像这样使用普通变量!如果你想对_*é等特殊字符进行编码,就会遇到问题。

    相反,您应该使用关联数组。这是一个完整的纯 Bash 工作示例:

    #!/bin/bash
    
    # read message to encode on stdin and outputs message to stdout
    
    declare -A lookup
    
    lookup=(
       [a]=n [b]=o [c]=p [d]=q [e]=r [f]=s [g]=t [h]=u [i]=v [j]=w [k]=x
       [l]=y [m]=z [n]=a [o]=b [p]=c [q]=d [r]=e [s]=f [t]=g [u]=h [v]=i
       [w]=j [x]=k [y]=l [z]=m
    )
    
    while IFS= read -r -d '' -n 1 char; do
       if [[ -z $char ]]; then
          # null byte
          printf '\0'
       elif [[ ${lookup["$char"]} ]]; then
          # defined character
          printf '%s' "${lookup["$char"]}"
       elif [[ $char = @(' '|$'\n') ]]; then
          # space and newline
          printf '%s' "$char"
       else
          # other characters passed verbatim with warning message
          printf >&2 "Warning, character \`%s' not supported\n" "$char"
          printf '%s' "$char"
       fi
    done
    

    我将此脚本称为 bananachmod +x banana 和:

    $ ./banana <<< "hello stackoverflow"
    uryyb fgnpxbiresybj
    $ ./banana <<< "uryyb fgnpxbiresybj"
    hello stackoverflow
    

    如果要对变量input的内容进行编码,并将编码后的文本存储在变量output中,只需将主循环修改为:

    output=
    linput=$input
    while [[ $linput ]]; do
       char=${linput::1}
       linput=${linput:1}
       if [[ ${lookup["$char"]} ]]; then
          # defined character
          output+=${lookup["$char"]}
       elif [[ $char = @(' '|$'\n') ]]; then
          # space and newline
          output+=$char
       else
          # other characters passed verbatim with warning message
          printf >&2 "Warning, character \`%s' not supported\n" "$char"
          output+=$char
       fi
    done
    

    在这种情况下,我省略了对空字节的检查,因为 Bash 变量不能包含空字节。


    这样进行,您可以真正编码任何您喜欢的字符(甚至是空字节 - 在第一个版本中 - 和换行符)。


    编辑(重新发表您的评论)。

    您想将此用于Caesar cipher 脚本。您担心的是您提示用户输入用于轮班的号码,并且您不知道如何在您的情况下使用此方法。关键是查找表的生成比较容易。

    这是一个完整的示例:

    #!/bin/bash
    
    chrv() {
       local c
       printf -v c '\\%03o' "$1"
       printf -v "$2" "$c"
    }
    
    ansi_normal=$'\E[0m'
    ansi_lightgreen=$'\E[02m'
    
    while true; do
       printf '%s' "Encryption key (number from ${ansi_lightgreen}1$ansi_normal to ${ansi_lightgreen}26$ansi_normal): "
       read -re ekey
       if [[ $ekey = +([[:digit:]]) ]]; then
          ((ekey=10#$ekey))
          ((ekey>=1 && ekey<=26)) && break
       fi
       printf 'Bad number. Try again.\n'
    done
    
    # Build the lookup table according to this number
    declare -A lookup
    
    for i in {0..25}; do
       for u in 65 97; do
          chrv "$((i+u))" basechar
          chrv "$(((i+ekey-1)%26+u))" "lookup["$basechar"]"
       done
    done
    
    printf '%s' "Input (only ${ansi_lightgreen}letters$ansi_normal and ${ansi_lightgreen}spaces${ansi_normal}): "
    IFS= read -re einput
    read -n1 -rep "Do you want output to be uppercase? ${ansi_lightgreen}(y/n)$ansi_normal " oup
    [[ ${oup,,} = y ]] && einput=${einput^^}
    
    output=
    linput=$einput
    while [[ $linput ]]; do
       char=${linput::1}
       linput=${linput:1}
       if [[ ${lookup["$char"]} ]]; then
          # defined character
          output+=${lookup["$char"]}
       elif [[ $char = @(' '|$'\n') ]]; then
          # space and newline
          output+=$char
       else
          # other characters passed verbatim with warning message
          printf >&2 "Warning, character \`%s' not supported\n" "$char"
          output+=$char
       fi
    done
    
    printf 'Original text: %s\n' "$einput"
    printf 'Encoded text: %s\n' "$output"
    

    纯 Bash,没有子 shell :)

    【讨论】:

    • 嘿。首先,感谢您花时间回答我。问题是每个字母都有一个对应的,但它是由脚本提示的(带有加密密钥,它是一个凯撒密码脚本)。
    • @FliiFe 没问题:您只需要构建lookup 表而不是对其进行硬编码。如果您向我们展示了如何提示和构建变量,将会很有趣。您很可能采用了糟糕的设计和损坏的方法(我将向您解释为什么它会损坏以及如何解决所有这些问题)。顺便说一句,我希望你能认出那里的 rot13 编码 :)
    • 好的。请看看这个:bitbucket.org/FliiFe/scripts/src/…。可以私信联系我吗?
    • @FliiFe 对不起,我不能私下联系你(我的工资负担不起:D)。我根据您当前的脚本编辑了我的答案以包含一个完整的工作示例(松散地)。
    • @FliiFe chrv 接受两个参数。第一个是数字(字符的代码点,例如,a97),第二个是存储字符的变量名。例如,调用chrv 97 foo 会将字符a 存储到变量foo 中。我们正在使用这个函数来构建查找表。
    【解决方案4】:

    使用 IFS=$'\n' 将内部字段分隔符从空格更改为换行符。

    【讨论】:

      【解决方案5】:

      要遍历字符串的字符,可以使用参数扩展:

      eiput='Hello World!'
      H="capital letter 'h'"
      l="I appear 3 times"
      
      strlen=${#eiput}
      for ((i=0; i<strlen; i++)); do
          char=${eiput:i:1}
          printf "%2d:%c:%s\n" $i "$char" "${!char}"
      done
      
       0:H:capital letter 'h'
       1:e:
       2:l:I appear 3 times
       3:l:I appear 3 times
       4:o:
       5: :
       6:W:
       7:o:
       8:r:
       9:l:I appear 3 times
      10:d:
      11:!:
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-04-21
        • 1970-01-01
        • 2014-07-28
        • 2016-11-07
        • 2011-08-03
        • 1970-01-01
        • 1970-01-01
        • 2018-12-21
        相关资源
        最近更新 更多