【问题标题】:Strange behavior using read for validate string (bash script)使用 read 验证字符串的奇怪行为(bash 脚本)
【发布时间】:2020-11-20 15:44:37
【问题描述】:

我正在使用 bash 创建一个 .sh 来验证 api 子文件夹版本

目标是验证这些字符串到APIS_BUILD var 并找到所有.proto 文件到./proto 文件夹以编译成protobuffer Go 文件

# define subfolder apis to build
APIS_BUILD=(
    prototests/v1/
    prototests2/v2
    testfolder
)
# the "testfolder" are a invalid folder

测试用例:

prototestes/v1                      # valid
prototestes/v1/cobranca             # valid
prototestes/v1/cobrnaca/faturamento # valid
outrapastacomarquivosproto/v1       # valid
prototests                          # invalid
/prototests                         # invalid

然后,我创建了这个脚本来验证APIS_BUILD 字符串数组

#!/usr/bin/env bash

# text color
RED='\033[0;31m'  # RED
BLUE='\033[0;34m' # Blue
NC='\033[0m'      # No Color

# Underline color
UCyan='\033[4;36m' # Cyan

# define subfolder apis to build
APIS_BUILD=(
    prototests/v1
    cobrancas/v1
)
DST_DIR="."       # define the directory to store the build-in protofiles
SRC_DIR="./proto" # define the proto files folder

# Compile proto file
# $1 = Filename to compile
function compile() {
    protoc --go_out=$DST_DIR --proto_path=proto --go_opt=M$1=services \
        --go_opt=paths=import --go-grpc_out=. \
        $1
}

# Validate api_build's
function validateApiBuilds() {
    for t in ${APIS_BUILD[@]}; do
        IFS="/"
        read -a SUBSTR <<<"$t"
        if [ ${#SUBSTR[@]} -lt 2 ]; then
            printf "${RED}The API_BUILD value ${UCyan}\"${t}\"${RED} are declare wrong, please declare [api_folder]/[version_folder] (example: prototest/v1)${NC}\n" 1>&2
            exit 1
        fi
    done
}

validateApiBuilds

for filename in $(find $SRC_DIR -name '*.proto'); do
    [ -e "$filename" ] || continue
    echo $filename
done
  • 子文件夹:

但我得到了一个奇怪的行为:

  • 如果使用validateApiBuilds 函数运行.sh 文件,则$filename 的返回值始终为.
  • 如果在没有validateApiBuilds 函数的情况下运行.sh 文件,则$filename 的返回将得到testservice.proto 文件

图片:

带有validateApiBuilds功能:

没有validateApiBuilds函数:


所有变量:

# define subfolder apis to build
APIS_BUILD=(
    prototests/v1
    cobrancas/v1
)
DST_DIR="."       # define the directory to store the build-in protofiles
SRC_DIR="./proto" # define the proto files folder

Bash 版本:

$ bash --version                                  
$ GNU bash, versão 4.4.19(1)-release (x86_64-pc-linux-gnu)

Obs.:我将 validateApiBuilds 函数更改为对字符串使用正则表达式验证到 API_BUILDS 变量。但我真的很想知道这种行为的原因。

编辑 2:make-proto.config 文件

# define subfolder apis to build
APIS_BUILD=(
    prototests/v1
    cobrancas/v1
)
DST_DIR="."       # define the directory to store the build-in protofiles
SRC_DIR="./proto" # define the proto files folder

【问题讨论】:

  • 请将您的脚本最小化为minimal reproducible example,这是最短的可能,无需更改即可运行以检查是否存在特定问题——我们不需要代码检查哪些变量存在,f / e,除非错误是专门针对这些检查的。除非您的问题与颜色有关,否则我们不需要打印彩色错误的代码;等
  • 顺便说一句,for filename in $(find $SRC_DIR -name '*.proto'); do 是一种反模式 - 请参阅 BashPitfalls #1 描述原因,以及 Using Find 描述要做什么。
  • @CharlesDuffy 该行为仅发生在上面的所有代码中,如果我只放置函数和“for”循环,则不会发生该行为
  • 当然不是。例如,如果您取出 if [[ -z "$APIS_BUILD" ]]; then 块,我无法相信它会停止发生。所以尽可能多地取出,同时只留下你不能取出的东西。如果这意味着一次注释掉一行或一个块,测试,如果问题不再出现则恢复,那么就这样做 - 另请参阅sscce.org 的“修剪技巧”部分,以获取有关如何构建清晰、最小化的指导复制者。
  • (另外,请注意echo $filename 本身可能存在错误——即BashPitfalls #14,或堆栈溢出问题I just assigned a variable, but echo $variable prints something else!,或shellcheck.net 警告SC2086——运行您的代码在这里提问之前通过shellcheck.net 是一个好主意)。

标签: linux bash shell


【解决方案1】:

更好地使用find

for filename in $(anything) 始终是一种反模式——它在 IFS 中拆分字符的值,然后将每个结果扩展为一个 glob。要使find 发出完全明确的字符串,请使用-print0

while IFS= read -r -d '' filename; do
    [ -e "$filename" ] || continue
    echo "$filename"
done < <(find "$SRC_DIR" -name '*.proto' -print0)

不要不必要地更改 IFS

更改您的代码,使IFS 的赋值与read 在同一行,这将使IFS 仅针对该命令进行修改。

也就是说,而不是:

IFS=/
read -a SUBSTR <<<"$t"

...你应该写:

IFS=/ read -a SUBSTR <<<"$t"

【讨论】:

  • 像魅力一样工作,感谢 IFS 解释和循环模式
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-04-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-15
  • 1970-01-01
相关资源
最近更新 更多