Bash 检测没有相应变量的字符

Bash detect characters that don't have corresponding var

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

我有这个:

eiput_splitted="$(echo $einput | sed -e 's/\(.\)/\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..)

你能帮帮我吗?

使用 IFS=$'\n'.

将内部字段分隔符从空格更改为换行符

您可以在 BASH 中使用它:

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

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

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

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

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

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:!:

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

相反,您应该使用关联数组。这是一个完整的 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 '[=10=]'
   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 Whosebug"
uryyb fgnpxbiresybj
$ ./banana <<< "uryyb fgnpxbiresybj"
hello Whosebug

如果要对变量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 脚本。您担心的是您提示用户输入用于轮班的号码,而您不知道如何在您的情况下使用此方法。关键是生成查找 table.

相当容易

这是一个完整的示例:

#!/bin/bash

chrv() {
   local c
   printf -v c '\%03o' ""
   printf -v "" "$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 且无子壳 :)