bash case语句中的正则表达式引发"意外令牌"错误



EDIT:我已经把我的代码推到了一个新的分支,只是为了这个问题

链接到分支

链接到外壳脚本


我正在尝试创建一个bash文件,该文件在预定义结构的所有目录(和子目录(上循环(即,我知道目录以特定的方式命名(。

一个明显的解决方案是使用正则表达式模式的case...esac语句。所以我去了StackOVerflow,在那里我发现了这篇文章,解释了如何做我需要的事情,所以我准备创建一个测试代码,这就是我想到的:

test
|
|__ python
|
|__ python-
|
|__ python-a
|
|__ python-0
|
|__ python0-
|
|__ python2
|
|__ pythona
|
|__ test.sh

并运行这个:

# test.sh
#!/bin/bash
shopt -s extglob;
for dir in $(ls); do
case $dir in
python*(-)*([a-zA-Z0-9]))
echo "yes -> $dir"
;;
*)
echo "no -> $dir"
;;
esac
done
shopt -u extglob;

这给了我以下输出:

yes -> python
yes -> python-
yes -> python-a
no -> python0-
yes -> python2
yes -> pythona
no -> test.sh

这似乎很好用。

我把这种方法应用到我的实际代码中:

# actual_code.sh
cd $PROGRAMS_DIR
for language in $(ls -l --group-directories-first | tail -n $(ls -l | awk '{print $1}' | grep d | wc -l) | awk '{print $9}'); do
cd $language
echo "language -> $language"
for algorithm in $(ls -l --group-directories-first | tail -n $(ls -l | awk '{print $1}' | grep d | wc -l) | awk '{print $9}'); do
cd $algorithm
echo "algo -> $algorithm"
shopt -s extglob;
case $language in
rust*(-)*([0-9]))
rustc "${algorithm}_run.rs" -o "${algorithm}_run"
COMMAND="./${algorithm}_run"
if [ $TEST -eq 1 ]; then
echo "> Running Rust tests for $algorithm"
rustc --test "${algorithm}_test.rs" -o "${algorithm}_test"
./${algorithm}_test
if [ $(echo $?) -ne 0 ]; then
exit 1
fi
fi
;;
go*(-)*([0-9]))
go build -o "${algorithm}_run" .
COMMAND="./${algorithm}_run"
if [ $TEST -eq 1 ]; then
echo "> Running Go tests for $algorithm"
go test
if [ $(echo $?) -ne 0 ]; then
exit 1
fi
fi
;;
java*(-)*([0-9]))
javac -cp .:$JUNIT:$HAMCREST *.java
COMMAND="java -cp .:${JUNIT}:${HAMCREST} ${algorithm}_run"
if [ $TEST -eq 1 ]; then
echo "> Running Java tests for $algorithm"
java -cp .:${JUNIT}:${HAMCREST} ${algorithm}_test
if [ $(echo $?) -ne 0 ]; then
exit 1
fi
fi
;;
c*(-)*([0-9]))
# TODO: Try to implement both the normal executable and the -O2 optimisation
gcc -Wall -c "${algorithm}.c" "${algorithm}_run.c"
gcc -o "${algorithm}_run" "${algorithm}.o" "${algorithm}_run.o"
COMMAND="./${algorithm}_run"
if [ $TEST -eq 1 ]; then
echo "> Running C tests for $algorithm"
gcc -Wall -c "${algorithm}.c" "${algorithm}_test.c" $UNITY
gcc -o "${algorithm}_test" "${algorithm}.o" "${algorithm}_test.o" "unity.o"
./${algorithm}_test
if [ $(echo $?) -ne 0 ]; then
exit 1
fi
fi
;;
python*(-)*([0-9]))
COMMAND="python ${algorithm}_run.py"
if [ $TEST -eq 1 ]; then
echo "> Running Python tests for $algorithm"
pytest .
if [ $(echo $?) -ne 0 ]; then
exit 1
fi
fi
;;
haxe*(-)*([0-9]))
COMMAND="haxe --main ${algorithm^}_Run.hx --interp"
if [ $TEST -eq 1 ]; then
echo "> Running Haxe tests for $algorithm"
haxe --main "${algorithm^}_Test.hx" --library utest --interp -D UTEST_PRINT_TESTS
if [ $(echo $?) -ne 0 ]; then
exit 1
fi
fi
;;
*)
echo "($language) has no compilation steps. Did you forget to update the benchmark script?"
;;
esac
shopt -u extglob;
.
.
.
some other random code
.
.
.
done
done

现在执行这个代码给我这个

./actual_code.sh: line 408: syntax error near unexpected token `('
./actual_code.sh: line 408: `                rust*(-)*([0-9]))'

我显然错过了一些东西,但它们在我看来是一样的。此外,echo部分也不起作用。它直接指向错误,这很奇怪,因为这是一种解释语言。

1。不解析ls

例如,我无法做到这一点:

for language in $(
ls -l --group-directories-first |
head -n $(ls -l | awk '{print $1}' | grep d | wc -l) |
awk '{print $9}'
)
do
# ...
done

解析ls除了一个好的练习之外,还有很多事情要做。使用globs更简单且100%准确(需要时可进行后期过滤(。

  • 对于文件:
#!/bin/bash
shopt -s nullglob
for file in ./*
do
[[ -f "$file" ]] || continue
# ...
done
  • 对于目录(甚至更简单(:
#!/bin/bash
shopt -s nullglob
for dir in ./*/
do
# ...
done
2.使用Makefiles

脚本中包含编译细节会使维护变得困难;您应该通过在每个算法目录中创建一个Makefile来将其委托给make

以下是Go算法目录的Makefile的基本示例:

build:
go build -o REPLACE_ME_WITH_THE_ALGORITHM_NAME_run
test:
go test

好的,让我们假设您已经编写了所需的Makefile(具有buildtest目标(;你现在可以大大简化你的脚本:

#!/bin/bash
shopt -s extglob nullglob
for buildpath in "$PROGRAMS_DIR"/@(rust|go|java|c|python|haxe)*(-)*([0-9])/*/
do
pushd -- "$buildpath" > /dev/null || continue
make build
if [[ $TEST == 1 ]]
then
make test || exit 1
fi
popd > /dev/null || exit 1
done

最后一句话:脚本中的变量应该是小写,除非导出或常量

Akcnowledgements


首先,感谢所有提出解决方案、为我指明正确方向并为本项目做出贡献的人。

@EdMorton-虽然shellcheck是一个很好的资源,并指出了我的脚本中许多(可能的(缺陷,但它想要应用的一些修复并不是修复,而是对实际功能不需要的传统做法的警告。我选择了跟随一些,但不是全部,因为有些会产生意想不到的结果。正如在关于原始问题的对话线程中所提到的,这指向了一些未知的潜在问题,并且在我的存储库中推送了一个新的分支,在那里,基准脚本将以通过shellcheck的方式重新编写。

@弗拉瓦多纳-感谢你提出Makefile的建议,这是我最初的决定,但由于我缺乏经验,我无法在剩下的时间内实现这一目标(这仍然是我最后一年的论文(。

解决方案当前到位


我已经决定,就目前而言,应用条件正则表达式匹配是本次提交中看到的方法(如下所示(。这是迄今为止最优雅的解决方案,但目前运行良好,不会增加任何显著的复杂性。

# Get rid of the leading './'
language=${language:2:${#language}}
# Capture for '-haxe' postfix of a language or any other pattern
# https://stackoverflow.com/a/18710850/5817020
if [[ $language =~ [a-zA-Z]*-[a-zA-Z0-9]* ]]; then
readarray -d "-" -t LANGUAGE <<< $language
lang="${LANGUAGE[0]}"
else
lang=$language
fi

最新更新