后台html模板/站内优化
个人主页:chian-ocean
文章专栏-Linux
前言:
Shell(外壳)是一个操作系统的用户界面,它提供了一种方式,使得用户能够与操作系统进行交互。Shell 是用户与操作系统之间的桥梁,允许用户通过命令行输入来执行各种操作,例如文件管理、程序执行、进程控制、系统监控等
常见的 Shell 类型:
Bash(Bourne Again Shell)
:- 是 Linux 和 macOS 等类 Unix 系统中常见的默认 Shell。它是 Bourne Shell 的增强版,支持丰富的特性,如命令补全、历史命令、数组等。
Zsh(Z Shell)
:- 是一个功能强大的 Shell,支持更丰富的自动化、命令补全、插件系统等特性。Zsh 常常被认为是最为用户友好的 Shell 之一。
Fish(Friendly Interactive Shell
):- 是一个具有用户友好界面和丰富特性(如自动提示、自动补全等)的现代 Shell。其设计注重简洁和易用性。
C Shell(csh)
:- 基于 C 语言语法的 Shell,主要用于早期的 Unix 系统。C Shell 提供了较强的脚本编程功能。
Korn Shell(ksh)
:- 是一个功能强大的 Shell,结合了 Bourne Shell 和 C Shell 的特性,并且提供了很多增强的功能。
shell外壳的实现
引入头文件
#include<string>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<assert.h>
-
#include<string>
:引入 C++ 的string
库,用于字符串处理。 -
#include<unistd.h>
:提供访问系统调用的接口,例如fork()
、execvp()
、getcwd()
等。 -
#include<sys/wait.h>
:包含等待子进程退出的函数。 -
#include<sys/types.h>
:包含系统数据类型的定义,如pid_t
(进程 ID 类型)。 -
#include<stdlib.h>
:提供一些标准库函数,如exit()
、getenv()
和putenv()
等。 -
#include<stdio.h>
:提供输入输出函数,如printf()
。 -
#include<string.h>
:提供字符串操作函数,如strtok()
、strcmp()
等。 -
#include<assert.h>
:提供调试宏assert()
,用于检测程序中的错误
宏定义
#define DELIM " \t"
#define LEFT "["
#define RIGHT "]"
#define LABLE "$"
#define LINE_SIZE 1024
#define ARGC_SIZE 32
#define EXIT_CODE 4
-
DELIM
:命令行参数的分隔符,包含空格和制表符。 -
LEFT
,RIGHT
,LABLE
:格式化命令行提示符的符号,用于显示用户、主机和当前工作目录。 -
LINE_SIZE
:最大命令行字符长度,设置为1024。 -
ARGC_SIZE
:最大命令行参数数量,设置为32。 -
EXIT_CODE
:用于退出的错误代码。
全局变量
int quit = 0;
int LASTCODE = 0;
char* argv[ARGC_SIZE];
char commondline[LINE_SIZE];
char pwd[ARGC_SIZE];
char myenv[ARGC_SIZE];
-
quit
:控制程序是否退出的标志。 -
LASTCODE
:记录上一个命令的退出状态码。 -
argv
:存储命令行解析后的参数。 -
commondline
:存储输入的命令行字符串。 -
pwd
:存储当前工作目录路径。 -
myenv
:存储环境变量。
const char* getusr()
{return getenv("USER");
}const char* gethostname()
{return getenv("HOSTNAME");
}
getusr
:返回当前用户的用户名。gethostname
:返回当前计算机的主机名。
获取当前工作目录
void getpwd()
{getcwd(pwd, sizeof(pwd));
}
getpwd
:调用getcwd
获取当前工作目录,并将结果存储在pwd
中。
交互式输入处理
void ineract(char* cline, int size)
{getpwd();printf(LEFT"%s@%s %s"RIGHT""LABLE" ", getusr(), gethostname(), pwd);char* s = fgets(cline, size, stdin);assert(s);(void)s;cline[strlen(cline) - 1] = '\0';
}
该 ineract
函数是命令行交互的核心部分,用于显示提示符并获取用户输入。以下是对代码逐行的解析:
函数定义
void ineract(char* cline, int size)
cline
:指向存储用户输入命令的字符数组的指针。size
:输入缓冲区的大小,表示cline
数组的最大容量。
获取当前工作目录并显示提示符
getpwd();
printf(LEFT"%s@%s %s"RIGHT""LABLE" ", getusr(), gethostname(), pwd);
-
getpwd()
:调用getpwd
函数来获取当前工作目录并存储到全局变量pwd
中。 -
printf
:显示命令行提示符。格式为[user@hostname pwd]$
,其中:getusr()
:获取当前用户名(通过环境变量USER
)。gethostname()
:获取当前主机名(通过环境变量HOSTNAME
)。pwd
:显示当前工作目录。
提示符通过格式化字符串显示,
LEFT
和RIGHT
用于添加方括号([
和]
)包围信息,而LABLE
是一个$
字符,表示命令行提示符。
获取用户输入
char* s = fgets(cline, size, stdin);
assert(s);
(void)s;
fgets(cline, size, stdin)
:从标准输入(键盘)读取用户输入,存储在cline
数组中,最多读取size-1
个字符。fgets
会自动在输入末尾添加一个\0
来终止字符串。assert(s)
:如果fgets
返回NULL
,程序将终止并输出错误信息。assert
是一种调试检查,确保输入读取成功。如果s
为NULL
,说明读取输入失败。(void)s
:(void)s
的作用是消除未使用变量s
的编译器警告,实际上这里并没有做任何事情。
去除输入末尾的换行符
cline[strlen(cline) - 1] = '\0';
strlen(cline) - 1
:计算输入字符串的长度,并将其最后一个字符(换行符\n
)替换为字符串结束符\0
。这一步去除fgets
读取时可能留下的换行符。
命令行解析
int AnalyzeCommandLine(char* cline)
{int i = 0;argv[i++] = strtok(cline, DELIM);while (argv[i++] = strtok(NULL, DELIM));return i - 1;
}
AnalyzeCommandLine
函数用于解析输入的命令行字符串,并将解析出的各个命令参数存储在 argv
数组中。以下是对该函数的逐行解析:
函数定义
int AnalyzeCommandLine(char* cline)
cline
:输入的命令行字符串(用户在命令行输入的完整命令)。该字符串将会被解析为多个命令和参数。
初始化参数索引
int i = 0;
i
:定义一个整数变量i
,用于跟踪argv
数组的索引位置,表示当前解析的命令参数的位置。
使用 strtok
解析命令行
argv[i++] = strtok(cline, DELIM);
strtok(cline, DELIM)
:strtok
是一个字符串分割函数,它通过指定的分隔符(DELIM
)将 cline
字符串分割成多个子字符串。DELIM
在此代码中定义为 " \t"
,即空格和制表符。
- 第一次调用
strtok()
时,它会返回cline
字符串中的第一个子字符串(即命令或第一个参数)。返回值会存储在argv[i]
中。 - 然后
i++
使得i
增加 1,指向下一个位置
继续解析命令行参数
while (argv[i++] = strtok(NULL, DELIM));
strtok(NULL, DELIM)
:在第一次调用strtok()
后,后续调用需要传入NULL
作为第一个参数,表示继续从上次分割的位置开始。strtok()
会继续根据分隔符分割剩余的命令行字符串,并返回下一个子字符串。- 这段代码通过
while
循环逐个提取命令行中的每个子字符串,并将其存储到argv[i]
中。每次调用strtok()
后,i++
将i
指向下一个数组位置。
返回参数的数量
return i - 1;
i - 1
:由于最后一次i++
会多加一次,因此函数返回i - 1
,即存储在argv
数组中的参数个数(命令行中的参数数量)。
执行常规命令
void NormalExecl(char* _argv[])
{pid_t id = fork();if (id < 0){perror("fork");return;}else if (id == 0){execvp(_argv[0], argv);exit(EXIT_CODE);}else{int status = 0;pid_t rid = waitpid(id, &status, 0);if (id){LASTCODE = WEXITSTATUS(status);}}
}
函数定义
void NormalExecl(char* _argv[])
_argv[]
:这是一个参数数组,用于传递命令及其参数。例如,_argv[0]
是命令,_argv[1]
是命令的第一个参数,依此类推。
创建子进程
pid_t id = fork();
fork()
:fork()
函数用于创建一个新进程。它将当前进程复制一份。新进程被称为子进程,原始进程是父进程。
- 如果
fork()
成功,它会返回两次:- 父进程:返回子进程的进程 ID(PID)。
- 子进程:返回 0。
- 如果
fork()
失败,它返回负值。
错误处理
if (id < 0)
{perror("fork");return;
}
id < 0
:如果fork()
返回负值,表示创建子进程失败。此时打印错误信息并返回。perror("fork")
:输出错误信息,说明fork()
失败的原因。
子进程执行命令
else if (id == 0)
{execvp(_argv[0], argv);exit(EXIT_CODE);
}
id == 0
:这是子进程中的代码块。如果 fork()
返回 0,表示当前代码在子进程中执行。
execvp(_argv[0], argv)
:子进程调用 execvp()
函数来执行命令。execvp()
会用指定的命令替换当前进程的映像。具体来说:
_argv[0]
是命令(例如ls
)。argv
是命令的参数数组,其中包含命令和它的所有参数(例如ls -l
)。
exit(EXIT_CODE)
:如果 execvp()
失败,子进程会退出,返回 EXIT_CODE
。如果 execvp()
成功,当前进程会被新的命令替代,exit()
不会被执行
父进程等待子进程结束
else
{int status = 0;pid_t rid = waitpid(id, &status, 0);if (id){LASTCODE = WEXITSTATUS(status);}
}
-
else
:这是父进程中的代码块,父进程需要等待子进程结束并获取其退出状态。 -
int status = 0;
:定义一个变量status
用来存储子进程的退出状态。 -
waitpid(id, &status, 0)
:父进程使用
waitpid()
函数等待子进程的结束。waitpid()
会阻塞父进程,直到指定的子进程结束,并返回子进程的退出状态。id
:是子进程的进程 ID,表示父进程等待这个子进程。&status
:存储子进程退出时的状态信息。0
:表示父进程等待子进程的退出,不对其状态做其他操作。
-
LASTCODE = WEXITSTATUS(status)
:获取子进程的退出状态码并存储在LASTCODE
中。WEXITSTATUS(status)
提取的是子进程的退出代码。
内建命令执行
int BuildExec(char* _argv[], int _argc)
{if (_argc == 2 && strcmp(_argv[0], "cd") == 0){chdir(_argv[1]);getpwd();sprintf(getenv("PWD"), "%s", pwd);return 1;}else if (_argc == 2 && strcmp(_argv[0], "export") == 0){strcpy(myenv, _argv[1]);putenv(myenv);return 1;}else if (_argc == 2 && strcmp(_argv[0], "echo") == 0){if (strcmp(_argv[1], "$?")){printf("%d\n", LASTCODE);LASTCODE = 0;}else if (strcmp(_argv[1], "$")){char* val = getenv(_argv[1] + 1);printf("%s\n", val);}else{printf("%s\n", _argv[1]);}}if (strcmp(_argv[0], "ls") == 0){_argv[_argc++] = "--color";_argv[_argc] = NULL;}return 0;
}
函数定义
int BuildExec(char* _argv[], int _argc)
_argv[]
:命令行解析后参数的数组,存储命令及其参数。_argc
:命令行参数的数量。
处理 cd
命令
if (_argc == 2 && strcmp(_argv[0], "cd") == 0)
{chdir(_argv[1]);getpwd();sprintf(getenv("PWD"), "%s", pwd);return 1;
}
strcmp(_argv[0], "cd") == 0
:检查命令是否为cd
。如果argv[0]
是"cd"
,则执行以下操作。chdir(_argv[1])
:改变当前工作目录到argv[1]
指定的路径。getpwd()
:调用getpwd()
获取新的工作目录并更新全局变量pwd
。sprintf(getenv("PWD"), "%s", pwd)
:更新环境变量PWD
,使其反映当前工作目录。return 1;
:表示已经处理了cd
命令,因此直接返回,不继续处理后面的代码。
处理 export
命令
else if (_argc == 2 && strcmp(_argv[0], "export") == 0)
{strcpy(myenv, _argv[1]);putenv(myenv);return 1;
}
strcmp(_argv[0], "export") == 0
:检查命令是否为export
。如果argv[0]
是"export"
,则执行以下操作。strcpy(myenv, _argv[1])
:将argv[1]
的值复制到myenv
字符数组中。argv[1]
应该是一个环境变量的设置(例如"VAR=value"
)。putenv(myenv)
:使用putenv()
将myenv
中的环境变量设置添加到当前环境中。return 1;
:表示已经处理了export
命令,直接返回。
处理 echo
命令
else if (_argc == 2 && strcmp(_argv[0], "echo") == 0)
{if (strcmp(_argv[1], "$?")){printf("%d\n", LASTCODE);LASTCODE = 0;}else if (strcmp(_argv[1], "$")){char* val = getenv(_argv[1] + 1);printf("%s\n", val);}else{printf("%s\n", _argv[1]);}
}
strcmp(_argv[0], "echo") == 0
:检查命令是否为echo
。如果是,继续执行以下代码。strcmp(_argv[1], "$?")
:检查是否要求输出上一个命令的退出状态码。如果argv[1]
是"$?"
,则输出上一个命令的退出代码LASTCODE
,并将LASTCODE
重置为 0。strcmp(_argv[1], "$")
:检查是否要求输出某个环境变量的值。如果argv[1]
是以$
开头(例如$HOME
),则获取该环境变量的值并打印。printf("%s\n", _argv[1]);
:如果既不是"$?"
也不是以$
开头,则直接输出argv[1]
,即用户传递给echo
的字符串。
特殊处理 ls
命令
if (strcmp(_argv[0], "ls") == 0)
{_argv[_argc++] = "--color";_argv[_argc] = NULL;
}
strcmp(_argv[0], "ls") == 0
:检查命令是否为ls
。如果是ls
命令,执行以下操作。_argv[_argc++] = "--color";
:给ls
命令添加--color
参数,这样ls
命令输出的文件列表会使用不同的颜色显示(通常是通过文件类型区分)。_argv[_argc] = NULL;
:将数组最后一个元素设置为NULL
,确保execvp()
在执行时能正确处理参数数组。
返回值
return 0;
- 如果命令不是内建命令(
cd
、export
、echo
)或者没有进行特殊处理(如ls
),则返回 0,表示该命令需要外部执行。
主程序逻辑
int main()
{while (!quit){//命令行提示ineract(commondline, sizeof(commondline));//命令解析int argc = AnalyzeCommandLine(commondline);//指令解析int n = BuildExec(argv, argc);if (!n) NormalExecl(argv);}return 0;
}
main
:主程序循环,不断提示用户输入命令。首先获取并解析命令行输入,然后判断是否为内建命令,若不是,则调用NormalExecl
执行外部命令。直到quit
被设置为 1 时,程序结束。
相关文章:

DIY Shell:探秘进程构建与命令解析的核心原理
个人主页:chian-ocean 文章专栏-Linux 前言: Shell(外壳)是一个操作系统的用户界面,它提供了一种方式,使得用户能够与操作系统进行交互。Shell 是用户与操作系统之间的桥梁,允许用户通过命令行…...

通过Redisson构建延时队列并实现注解式消费
目录 一、序言二、延迟队列实现1、Redisson延时消息监听注解和消息体2、Redisson延时消息发布器3、Redisson延时消息监听处理器 三、测试用例四、结语 一、序言 两个月前接了一个4万的私活,做一个线上商城小程序,在交易过程中不可避免的一个问题就是用户…...

SQL Server配置管理器无法连接到 WMI 提供程序
目录 第一步第二部 第一步 发现没有资源管理器 在文件夹找到管理器 打开发现报这个错误 配置管理器无法连接到 WMI 提供程序第二部 https://blog.csdn.net/thb369208315/article/details/126954074...

Linux内核源码:ext4 extent详解
在 Linux 系统的庞大体系中,文件系统就像是一个井然有序的图书馆,而 ext4 文件系统则是这座图书馆中极为重要的 “藏书室”,它负责高效管理和存储数据。在 ext4 众多的奥秘中,ext4 extent 犹如一颗璀璨的明珠,起着关键…...

Maven jar 包下载失败问题处理
Maven jar 包下载失败问题处理 1.配置好国内的Maven源2.重新下载3. 其他问题 1.配置好国内的Maven源 打开⾃⼰的 Idea 检测 Maven 的配置是否正确,正确的配置如下图所示: 检查项⼀共有两个: 确认右边的两个勾已经选中,如果没有请…...

自指学习:AGI的元认知突破
文章目录 引言:从模式识别到认知革命一、自指学习的理论框架1.1 自指系统的数学定义1.2 认知架构的三重反射1.3 与传统元学习的本质区别二、元认知突破的技术路径2.1 自指神经网络架构2.2 认知效能评价体系2.3 知识表示的革命三、实现突破的关键挑战3.1 认知闭环的稳定性3.2 计…...

排序算法--希尔排序
希尔排序是插入排序的改进版本,适合中等规模数据排序,性能优于简单插入排序。 // 希尔排序函数 void shellSort(int arr[], int n) {// 初始间隔(gap)为数组长度的一半,逐步缩小for (int gap n / 2; gap > 0; gap …...

Java 2024年面试总结(持续更新)
目录 最近趁着金三银四面了五六家公司吧,也整理了一些问题供大家参考一下(适合经验三年左右的)。 面试问题(答案是我自己总结的,不一定正确): 总结: 最近趁着金三银四面了五六家公…...

TensorFlow是个啥玩意?
TensorFlow是一个开源的机器学习框架,由Google开发。它可以帮助开发者构建和训练各种机器学习模型,包括神经网络和深度学习模型。TensorFlow的设计理念是使用数据流图来表示计算过程,其中节点表示数学运算,边表示数据流动。 Tens…...

不可信的搜索路径(CWE-426)
漏洞描述:程序使用关键资源时(如动态链接库、执行文件、配置文件等)没有明确的指定资源的路径,而是依赖操作系统去搜索资源,这种行为可能被攻击者利用,通过在搜索优先级较高的目录放置不良资源,…...

Linux——基础命令
$:普通用户 #:超级用户 cd 切换目录 cd 目录 (进入目录) cd ../ (返回上一级目录) cd ~ (切换到当前用户的家目录) cd - (返回上次目录) pwd 输出当前目…...

利用TensorFlow.js实现浏览器端机器学习:一个全面指南
引言 随着深度学习技术的不断发展,机器学习已从传统的服务器端运算逐渐转向了前端技术。TensorFlow.js 是 Google 推出的一个用于在浏览器中进行机器学习的开源库,它允许开发者在浏览器中直接运行机器学习模型,而无需依赖后端服务器。Tensor…...

利用HTML和css技术编写学校官网页面
目录 一,图例展示 二,代码说明 1,html部分: 【第一张图片】 【第二张图片】 【第三张图片】 2,css部分: 【第一张图片】 【第二张图片】 【第三张图片】 三,程序代码 一,…...

SpringSecurity密码编码器:使用BCrypt算法加密、自定义密码编码器
1、Spring Security 密码编码器 Spring Security 作为一个功能完备的安全性框架,一方面提供用于完成加密操作的 PasswordEncoder 组件,另一方面提供一个可以在应用程序中独立使用的密码模块。 1.1 PasswordEncoder 抽象接口 在 Spring Security 中,PasswordEncoder 接口代…...

笔记:新能源汽车零部件功率级测试怎么进行?
摘要:本文旨在梳理主机厂对新能源汽车核心零部件功率级测试需求,通过试验室的主流设备仪器集成,快速实现试验方案搭建,并体现测试测量方案的时效性、便捷性优势。目标是通过提升实现设备的有效集成能力、实现多设备测试过程的有效协同、流程化测试,可快速采集、分析当前数…...

ES6中的map和原生的对象有什么区别?
在 ES6 中,Map 和原生的对象(Object)都是用来存储键值对数据的集合,但它们有显著的区别。以下是它们之间的主要区别: 1. 键的类型 Object: 只允许使用字符串或符号作为键。其他类型的键(如数字或对象&…...

2502vim,vim文本对象中文文档
介绍 文本块用户(textobj-user)是一个可帮助你毫不费力地创建自己的文本对象的Vim插件. 因为有许多陷阱需要处理,很难创建文本对象.此插件隐藏了此类细节,并提供了声明式定义文本对象的方法. 你可用正则式来定义简单的文本对象,或使用函数来定义复杂的文本对象.如… 文本对…...

spring security与gateway结合进行网关鉴权和授权
在Spring Cloud Gateway中集成Spring Security 6以实现鉴权和认证工作,可以在网关代理层完成权限校验和认证。这种架构通常被称为“边缘安全”或“API网关安全”,它允许你在请求到达后端服务之前进行集中式的安全控制。 以下是如何配置Spring Cloud Gat…...

LabVIEW在电机自动化生产线中的实时数据采集与生产过程监控
在电机自动化生产线中,实时数据采集与生产过程监控是确保生产效率和产品质量的重要环节。LabVIEW作为一种强大的图形化编程平台,可以有效实现数据采集、实时监控和自动化控制。详细探讨如何利用LabVIEW实现这一目标,包括硬件选择、软件架构设…...

log4j2日志配置文件
log4j2配置文件每个项目都会用到,记录一个比较好用的配置文件,方便以后使用时调取,日志输出级别为debug,也可以修改 <?xml version"1.0" encoding"UTF-8"?> <Configuration monitorInterval"180" packages""><prope…...

用Deepseek做EXCLE文件对比
背景是我想对比两个PO系统里的一个消息映射,EDI接口的mapping有多复杂懂的都懂,它还不支持跨系统版本对比,所以我费半天劲装NWDS,导出MM到excle,然后问题来了,我需要对比两个excel文件里的内容,…...

Tailwind CSS v4.0 升级与 Astro 5.2 项目迁移记录
本文博客链接 https://ysx.cosine.ren/tailwind-update-v4-migrate 自用小记。 Tailwind CSS v4.0 - Tailwind CSS 新的高性能引擎 - 完整构建的速度速度快 5 倍,增量构建的速度快于 100 倍以上 —— 以微秒为单位进行测量。为现代 Web 设计 - 建立在前沿的 CSS 特…...

TongSearch3.0.4.0安装和使用指引(by lqw)
文章目录 安装准备手册说明支持的数据类型安装控制台安装单节点(如需集群请跳过这一节)解压和启动开启X-Pack Security和生成p12证书(之后配置内置密码和ssl要用到)配置内置用户密码配置ssl(先配置内置用户密码再配ssl)配置控制台…...

低代码产品表单渲染架构
在React和Vue没有流行起来的时候,低代码产品的表单渲染设计通常会使用操作Dom的方式实现。 下面是一个表单的例子: 产品层 用户通过打开表单,使用不同业务场景业务下的表单页面,中间的Render层就是技术实现。 每一个不同业务的表单…...

windows 剪切板的写入、读取,包括图片,文本内容
介绍 在windows开发过程中,我们可能会需要对系统剪切板进行操作,其中包括读取剪切板数据和将数据写入到剪切板中 设置剪切板内容 /*** brief 设置剪切板内容* param[in] pszData 指向缓冲区的指针* param[in] nDataLen 缓冲区长度* return 成功返回TRU…...

Matplotlib 高级图表绘制与交互式可视化(mpld3)
我们先重新回忆一下它的主要作用: 一、Matplotlib 简介 Matplotlib 是 Python 中一个非常强大的可视化库,广泛用于数据可视化、科学计算和工程领域。它提供了丰富的绘图功能,可以生成各种静态、动态和交互式的图表。以下是 Matplotlib 的主要功能及其详细讲解。 二、基本…...

(9)gdb 笔记(2):查看断点 info b,删除断点 delete 3,回溯 bt,
(11) 查看断点 info b: # info b举例: (12)删除断点 delete 2 或者删除所有断点: # 1. 删除指定的断点 delete 3 # 2. 删除所有断点 delete 回车,之后输入 y 确认删除所有断点 举…...

专业学习|通过案例了解蒙特卡罗模拟实操步骤与含义
一、蒙特卡罗模拟介绍 蒙特卡罗模拟(Monte Carlo Simulation)是一种基于随机采样的数值计算方法,用于解决具有不确定性或复杂概率分布的问题。其核心思想是通过多次随机抽样来逼近系统的行为或目标函数的真实值,进而对系统进行评估…...

云端智慧:创业公司如何以全球视野选择最佳平台,实现业务新高度
2016年8月,一个名叫Bryce Adams的人辞去了自己原本很稳定的工作,开始追逐梦想:为使用WooCommerce(一种开源的WordPress数字商务插件)的公司开发一种能提供各类报表解决方案的应用。为此他成立了Metorik公司ÿ…...

【工具变量】中国省级八批自由贸易试验区设立及自贸区设立数据(2024-2009年)
一、测算方式:参考C刊《中国软科学》任晓怡老师(2022)的做法,使用自由贸易试验区(Treat Post) 表征,Treat为个体不随时间变化的虚拟变量,如果该城市设立自由贸易试验区则赋值为1,反之赋值为0&am…...