tsoding 大大写项目用 shell
脚本+ clang
来编译,CMU15445 的 Bustub 项目用 CMake
来编译,我来简单搜集信息
Shell
GUI -- 用户图形界面
CLI -- 命令行界面
Shell 相当于用户与操作系统之间的中介
More Infomation: Shell 编程范例
更加系统的学习,推荐:The Linux Command Line
脚本的运行
假设我编写好了一个脚本 test.sh
,先给该文件可执行权限:
然后执行即可
或是直接
脚本的编写
脚本开头
用于指定该脚本的解释器
剩下编译的部分
1
| g++ -std=c++17 -o [filename] [filename].cpp
|
就好了
如果还有更复杂的要求可以问 GPT
GPT 写的一个脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| #!/bin/bash
# 设置编译器和编译选项 CXX=g++ CXXFLAGS="-std=c++17 -O2 -Wall" OUTPUT="program"
# 定义源文件和目标文件 SRC_DIR="src" SRC_FILES=$(find $SRC_DIR -name "*.cpp") OBJ_FILES=""
# 编译每个源文件为目标文件 for src in $SRC_FILES; do obj="${src%.cpp}.o" $CXX $CXXFLAGS -c $src -o $obj OBJ_FILES="$OBJ_FILES $obj" done
# 链接目标文件生成可执行文件 $CXX $OBJ_FILES -o $OUTPUT
# 清理目标文件(可选) rm -f $OBJ_FILES
echo "编译完成,生成可执行文件:$OUTPUT"
|
g++
的 -c
是仅编译不连接,生成 .o
文件
$ 在 Shell 中的用法
用于变量引用
1 2 3 4 5
| $ name="John" $ echo $name John $ echo "Hello, ${name}Doe" Hello, JohnDoe
|
用于命令替换
1 2 3
| $ current_date=$(date) $ echo "Today is $current_date" Today is Sat Jan 12 12:34:56 UTC 2025
|
用于表示一些参数
$0
表示当前脚本的名称
$1
, $2
,... 表示脚本的参数
$#
表示参数个数
$?
表示上一个命令的退出状态码(0 表示成功)
CMake
cmake
可以自动构建 Makefile,所以这个编译过程分为两个过程,先构建 Makefile 文件,再 make
CMakeLists.txt
是配置文件
构建过程
1 2 3
| cmake -B build # 生成构建目录 cmake --build build # 执行构建 ./build/project_name # 运行可执行文件
|
一些语法
基本的三个东西:
1 2 3 4 5 6 7
| cmake_minimum_required(VERSION 3.10)
project(project_name)
add_executable(project_name pro1.cpp pro2.cpp)
|
添加库目标,可多次复用
1 2 3 4
| add_library(lib_name STATIC lib.cpp) add_executable(project pro.cpp)
target_link_libraries(project lib_name)
|
子目录中也有 CMakelist.txt,对子目录运行 CMake
1
| add_subdirectory(dir_name)
|
若头文件被装到了一个子文件夹中,下面的语句给库头文件支持,PUBLIC
表示这些头文件可以被任何依赖该库的文件通过 #include<当前文件夹名/头文件名>
引用
1
| target_include_directories(lib_name PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
打印调试信息
1
| message(STATUS "Current source dir: ${CMAKE_CURRENT_SOURCE_DIR}")
|
别的问 GPT
e.g. 1
记录我构建做算法题的工作流的过程
我的一个基本的文件布局
1 2 3 4 5 6 7 8 9 10 11 12
| /ACM/2025/ ├── 001/ │ ├── CSP-S2024 │ | ├── A.cpp | | ├── B.cpp | | ├── C.cpp | | └── D.cpp │ └── NOIP2024 ├── 002/ ├── 003/ ├── aliases.sh └── run_code.sh
|
默认用的 shell 是 zsh,可以拿 echo $SHELL
查看
zsh 的配置文件在 ~/.zshrc
aliases.sh
是用来定义函数和别名的,方便编译运行文件
run_code.sh
是编译并运行 .cpp
文件的脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| # aliases.sh #!/bin/zsh
acm() { if [ $# -lt 2 ]; then echo "Please input the correct parameters: " echo " acm np [folder_name] [create_file_quantity]" echo " acm run [cpp_file_name]" return 1 fi identifier=$1 # 标识符(如 np) if [ "$identifier" = "np" ]; then if [ $# -ne 3 ]; then echo "Please input the correct parameters: " echo " acm np [folder_name] [create_file_quantity]" echo " acm run [cpp_file_name]" return 1 fi folder_name=$2 # 新建文件夹名 cpp_count=$3 # 要创建的 cpp 文件数量(如 3) # 检查参数3是否是有效数字 if ! [[ "$cpp_count" =~ ^[0-9]+$ ]] || [ "$cpp_count" -le 0 ]; then echo "请输入有效的数字参数!" return 1 fi # 获取当前目录下按时间排序的文件夹,选择最新的一个 # 查找符合条件的 3 位数字文件夹 latest_folder=$(find . -maxdepth 1 -type d | grep -E './[0-9]{3}$' | xargs -I {} stat -f "%m %N" {} | sort -n | tail -n 1 | awk '{print $2}') latest_folder=$(basename "$latest_folder") # 提取文件夹名 latest_folder="${latest_folder}/" # 确保添加尾部斜杠 if [ -z "$latest_folder" ]; then echo "当前目录下没有文件夹!" return 1 fi # 在最新文件夹下创建目标文件夹 target_folder="${latest_folder}${folder_name}" # 如果目标文件夹不存在,则创建它 if [ ! -d "$target_folder" ]; then mkdir "$target_folder" echo "创建文件夹 $target_folder" fi # 在目标文件夹中创建指定数量的 cpp 文件 for i in $(seq 1 $cpp_count); do # 获取字母序列 A、B、C... letter=$(printf \\$(printf '%03o' $((65 + i - 1)))) touch "${target_folder}/${letter}.cpp" echo "${letter}.cpp 创建完成" done echo "操作完成!" elif [ "$identifier" = "run" ]; then if [ $# -ne 2 ]; then echo "Please input the correct parameters: " echo " acm np [folder_name] [create_file_quantity]" echo " acm run [cpp_file_name]" return 1 fi /Users/wsy/Back_end/ACM/2025/run_code.sh $2 else echo "Please input the correct parameters: " echo " acm np [folder_name] [create_file_quantity]" echo " acm run [cpp_file_name]" return 1 fi }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| # run_code.sh #!/bin/bash
# 检查是否提供了参数 if [ "$#" -ne 1 ]; then echo "Usage: $0 <source_file>" exit 1 fi
# 获取传入的文件路径 SOURCE_FILE="$1"
# 检查文件是否存在 if [ ! -f "$SOURCE_FILE" ]; then echo "Error: File '$SOURCE_FILE' does not exist." exit 1 fi
# 提取文件目录和文件名(不带扩展名) DIR=$(dirname "$SOURCE_FILE") BASE_NAME=$(basename "$SOURCE_FILE" .cpp)
# 设置输出的可执行文件路径(无后缀) OUTPUT_FILE="$DIR/$BASE_NAME"
# 编译源文件 echo "Compiling '$SOURCE_FILE'..." g++ -o "$OUTPUT_FILE" "$SOURCE_FILE"
# 检查编译是否成功 if [ "$?" -ne 0 ]; then echo "Error: Compilation failed." exit 1 fi
# 执行生成的可执行文件 echo "Executing '$OUTPUT_FILE'..."
touch "${OUTPUT_FILE}.out"
# 这里我将输出重定向到 .out 文件中再输出,解决了输入输出乱套的情况 "$OUTPUT_FILE" >"${OUTPUT_FILE}.out" echo "Output:" cat "${OUTPUT_FILE}.out"
|
GPT 写的脚本就是安全可靠
因为 aliases.sh
定义的别名只有在 source
后才会适用,我们希望每次进入这个文件夹后就可以使用 run
命令了,这个需要动态加载 aliases.sh
文件,在 .zshrc
中利用 chpwd 钩子(每次切换目录的时候会执行)动态加载 aliases.sh
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| # .zshrc
# 定义函数,用于寻找并加载 aliases.sh function load_aliases() { local dir=$(pwd) while [ "$dir" != "/" ]; do if [ -f "$dir/aliases.sh" ]; then source "$dir/aliases.sh" return fi dir=$(dirname "$dir") done }
# 使用 chpwd 钩子,每次目录切换时执行 load_aliases function chpwd() { load_aliases }
# 初始加载,确保第一次打开 shell 时也加载 load_aliases
|
结合上我之前写过的 nvim 配置,这样的话就可以完整的构建一个做算法题时编译运行的工作流了,我发现这个 CLI 具有高度的定制化,是时候将 GUI 迁移到 CLI 了