首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Bash编译并运行用于编码竞赛的c++代码

Bash编译并运行用于编码竞赛的c++代码
EN

Code Review用户
提问于 2020-12-06 18:22:46
回答 2查看 661关注 0票数 6

这是一个简单的bash脚本,用于编译和运行单个C++文件,用于编码竞赛。

功能:

  • 检测它旁边是否有相应的.in文件,如果有,则将其用作stdin 。
    • 例如,如果文件problem1.cppproblem1.in位于同一个目录中,脚本将从problem1.in重定向stdin

  • 将每个文件编译到一个临时目录,这样它就不会干扰工作目录
  • 需要时可配置g++警告标志

注意:\033[32m\033[0m是使文本变成绿色的终端颜色代码。

代码语言:javascript
运行
复制
#!/bin/bash
# Compiles and runs .cpp code
# if there exists an .in file next to the .cpp file
# it will use that as input

if [ -z $1 ]; then
  echo -e "Please choose an input file"
  exit 1
fi

FILE="$1"
FILE_IN="${FILE%.*}.in"

clear
echo -e "\033[32mCompiling...\033[0m"
TMPFILE=$(mktemp /tmp/run-cpp.XXXXXXXXXX)
WARNING_FLAGS="-Wuninitialized -Wmaybe-uninitialized"
g++ $FILE -std=c++17 $WARNING_FLAGS -O3 -o $TMPFILE

if [ $? -eq 0 ]; then
  echo -e "\033[32mRunning...\033[0m"
  if [ -f $FILE_IN ]; then
    $TMPFILE < $FILE_IN
  else
    $TMPFILE
  fi
  ERROR=$?
fi
rm $TMPFILE 2> /dev/null
exit $ERROR

用法:./runcpp.sh /path/to/my-cpp-file.cpp

EN

回答 2

Code Review用户

回答已采纳

发布于 2020-12-10 17:37:37

#!/bin/bash

我不明白为什么要使用Bash --使用标准POSIX shell应该没有问题。

如果;那么

我建议在那里使用"$1",尽管额外的参数会导致[由于其他原因返回false。作为单个参数传递不会发出任何错误消息。

回显-e“请选择输入文件”

echo -e不是可移植的,在这里也没有必要。

FILE="$1“FILE_IN="${FILE%.*}.in”

避免为您自己的程序中的变量使用全大写名称--这些名称通常是为更改程序行为的环境变量而保留的。

清除

我认为这有点粗鲁--我们中的一些人喜欢将结果与之前的结果进行比较。如果我想在运行前清除,我可以很容易地键入。

回波-e“\033[32.\033[0m]”

别像那样嵌入终端特定的转义码!即使最近的终端支持ANSI转义,还有其他的-如果您重定向到文件,您不希望它随处可见的控制字符。使用tput为您的$TERM生成正确的转义符(以及更易读的代码)。

TMPFILE=$(mktemp /tmp/run-cpp.XXXXXXXXXX)

这不是一个描述性很强的变量名。说它是为了什么(创建的可执行文件)是更有信息的。为什么硬编码/tmp作为目录?如果设置了$TMPDIR (可能是具有每个用户临时目录的系统,或者有快速或大型临时存储的选择),则更喜欢使用set。

WARNING_FLAGS="-Wuninitialized -W或许-未初始化“

这是一套相当松懈的警告。如果您关心源的质量,请再添加几个。我建议-Wall -Wextra -Wwrite-strings -Wno-parentheses -Wpedantic -Warray-bounds -Weffc++。如果您只关心性能,那么也许-Wall -Wextra -Wno-parentheses -Warray-bounds就足够了。

由于我们只使用这个变量一次,也许我们应该内联它的使用,这样就不会触发一个Shellcheck警告,在这里我们(正确地)不用引号展开它。

g++ $FILE -std=c++17 $WARNING_FLAGS -O3 -o $TMPFILE

"$FILE"也在这里。我会写"$TMPFILE",尽管我们构建它是为了一个安全的名字。

如果;那么

反模式--只需在if之后直接使用前面的命令。或者,启用shell的-e标志,在编译失败时退出。

回声-e“\033[32 32mRunning.\033[0m]”

tput又来了。

如果;那么

再引用一遍。

$TMPFILE < $FILE\_IN else $TMPFILE fi

由于我们不使用stdin,所以可以使用stdin重定向exec,而不需要两个不同的命令。

ERROR=$?fi rm $TMPFILE 2> /dev/null

如果我们提前退出,为什么不删除临时文件呢?

出口$ERROR

如果我们将删除临时文件作为退出陷阱,则不需要存储错误。

修改代码

代码语言:javascript
运行
复制
#!/bin/sh
# Compiles and runs C++ source code. A corresponding file
# ending with .in will be used as input, if present

set -eu

if [ $# -ne 1 ] || [ -z "$1" ]
then
  echo "Usage: $0 SOURCE"
  exit 1
fi

executable=$(mktemp -t run-cpp.XXXXXXXXXX)
trap 'rm $executable' EXIT

green=$(tput setaf 2)
normal=$(tput sgr0)

echo "${green}Compiling...${normal}"
g++ -o "$executable" -std=c++17 -O3                     \
    -Wall -Wextra -Wwrite-strings -Wno-parentheses      \
    -Wpedantic -Warray-bounds -Weffc++                  \
    "$1"

echo "${green}Running...${normal}"
input=${1%.*}.in
if [ -f "$input" ]
then exec <"$input"
fi

"$executable"

您也可能对我对待类似情况的方法感兴趣,它使用Make而不是shell。

票数 4
EN

Code Review用户

发布于 2020-12-11 15:00:59

另一篇评论提出了我想要得到的大部分观点,所以我只需再补充三点。

不要忽略mktemp

的返回值

出于安全考虑,如果所选择的名称已经存在,mktemp将拒绝创建文件。这是为了避免安全缺陷。您可能会被保存,因为/tmp/应该始终设置粘性位,从而防止文件所有者以外的用户删除它。不过,我还是推荐这样的建筑:

代码语言:javascript
运行
复制
TMPFILE=$(mktemp /tmp/example.XXXXXXXXXX) || exit 1

另一种选择是省略输出文件名,只允许编译器在本地目录中创建a.out,完全绕过这个问题,并缩短脚本。

直接使用结果而不是测试$?

该守则包括:

代码语言:javascript
运行
复制
g++ $FILE -std=c++17 $WARNING_FLAGS -O3 -o $TMPFILE
if [ $? -eq 0 ]; then

但这可以变得更直接一点:

代码语言:javascript
运行
复制
if g++ $FILE -std=c++17 $WARNING_FLAGS -O3 -o $TMPFILE ; then

或者更好,看下一个建议。

考虑使用函数

我建议创建一些函数,比如runcompile,这样可以使脚本更容易理解。例如:

代码语言:javascript
运行
复制
compile() {
    WARNING_FLAGS="-Wuninitialized -Wmaybe-uninitialized"
    g++ "$1" -std=c++17 $WARNING_FLAGS -O3 -o "$2"
}

if compile "$FILE" "$TMPFILE"; then
票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/253140

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档