Python 通过Shell向所有函数的开头和结尾添加日志语句 我有一个项目,它有1000个以上的C++源文件存储在同一目录下的不同文件夹中。我想在这些.cpp文件中定义的所有函数的开头和结尾添加日志代码。请参见下面的示例: //SomeSrcFile.cpp //Sample ReturnType SomeClass::SomeFunc1(InputParameters) { ...... } ReturnType SomeClass::SomeFunc2(InputParameters) { ...... } 输出
如何为这种工作编写shell脚本?这里可以用awk吗 更新Python 通过Shell向所有函数的开头和结尾添加日志语句 我有一个项目,它有1000个以上的C++源文件存储在同一目录下的不同文件夹中。我想在这些.cpp文件中定义的所有函数的开头和结尾添加日志代码。请参见下面的示例: //SomeSrcFile.cpp //Sample ReturnType SomeClass::SomeFunc1(InputParameters) { ...... } ReturnType SomeClass::SomeFunc2(InputParameters) { ...... } 输出,python,linux,bash,shell,awk,Python,Linux,Bash,Shell,Awk,如何为这种工作编写shell脚本?这里可以用awk吗 更新 我认为通过AWK可以像那样做。但我还没有想出用我的方式去做 我只想使用Bash或Python来实现这一点,因为它们是我现在唯一熟悉的工具 更新2 也许这项工作真的很辛苦,超出了我的预料。但是如果我不在乎准确性呢?如果我不关心函数包含在注释等中的情况,该怎么办?有没有一种简单的方法可以做到这一点?请参阅您粘贴的帖子,下面是代码: awk 'BEGIN{X=FS} { if ($0~/void/ && $0 ~/\(
也许这项工作真的很辛苦,超出了我的预料。但是如果我不在乎准确性呢?如果我不关心函数包含在注释等中的情况,该怎么办?有没有一种简单的方法可以做到这一点?请参阅您粘贴的帖子,下面是代码:
awk 'BEGIN{X=FS}
{ if ($0~/void/ && $0 ~/\(/) split($0,a,FS);split(a[2],b,"(")
FS="";OFS="";
for (i=1; i<=NF; i++)
if ($i == "{") {
if (++d == "1") $i=sprintf("{\n\tchar FuncName[] = \"%s()\";\n\tprintf(\"%%s begins\\n\", FuncName);\n",b[1]);
} else {if ($i == "}") {
if (d-- == "1") $i=sprintf("\n\tprintf(\"%%s end\\n\", FuncName);\n\t} ",b[1]);
}
}
FS=X;OFS=X
}1' infile.cpp
到
如果您不介意它不是健壮的,那么对于简单一致的情况,使用GNU awk作为第三个参数to match()和字符类的缩写(例如
\w
):
对于其他AWK,只需在regexp中使用[[:space:][]
而不是\s
和[:alnum:][uuu]
而不是\w
,并使用match()
与substr()
和/或sub()
的组合从匹配regexp的字符串中提取函数名,例如:
$ cat tst2.awk
BEGIN {
beg = "\tchar FuncName[] = \"%s()\";\n\tprintf(\"%%s begins\\n\", FuncName);\n"
end = "\n\tprintf(\"%%s ends\\n\", FuncName);"
}
/^[[:space:]]*[[:alnum:]_]+[[:space:]]+([[:alnum:]_]+::[[:alnum:]_]+)[(][^)]*[)]/ {
funcName = $0
gsub(/^[[:space:]]*[[:alnum:]_]+[[:space:]]+|[(][^)]*[)].*/,"",funcName)
}
/{/ && (++braceCnt == 1) { $0 = $0 ORS sprintf(beg,funcName) }
/}/ && (--braceCnt == 0) { $0 = sprintf(end,funcName) ORS $0 }
{ print }
我建议在C++中使用一种不同的方法。 <>这只会简化shell脚本的需要,但会得到更好的C++代码。 简而言之,我建议创建一个跟踪类和一组宏来部署它。这有很多好处:
- 易于添加、删除、更改、打印输出
- 自动处理文件名、函数名、行号等
- 无论函数存在于何处都有效(这比您的方法有很大改进)
// File: Trace.h
class Trace
{
public:
// print function entry message
Trace(const char *name,
const char *file,
const int line,
int traceOn = -1, // used for enabling/disabling Trace printing
// within a particular call chain.
const void* thisPtr = 0);
// print function exit message
~Trace();
static bool active;
private:
std::string* theFunctionName;
std::string* theFileName;
int theLineNumber;
const void* theThisPtr;
static int layer; // used for printout indenting
static std::list<clock_t> startTimeList; // used for printing timing information
bool previouslyActive;
};
现在,要使用跟踪类,您需要执行以下操作:
//SomeSrcFile.cpp
//Sample
#define TRACE_ENABLED // comment this out to disable tracing in this file.
#include "Trace.h"
ReturnType SomeClass::SomeFunc1(InputParameters)
{
TRACE__
......
}
ReturnType SomeClass::SomeFunc2(InputParameters) {
TRACE__
......
}
现在,跟踪类的一个潜在实现是:
//文件:Trace.C
#include "Trace.h"
bool Trace::active = true;
int Trace::layer = 0;
std::list<clock_t> Trace::startTimeList;
Trace::Trace(const char *name,
const char *file,
const int line,
int traceOn, // default: -1
const void* thisPtr) // default: 0
: theFunctionName(0),
theFileName(0),
theLineNumber(0),
theThisPtr(thisPtr),
previouslyActive(active)
{
if (active)
{
theFunctionName = new std::string(name);
theFileName = new std::string(file);
theLineNumber = line;
std::cout<<"---> Trace: ";
for( int L = 0; L < layer; ++L )
{
std::cout<<" ";
}
std::cout
<<"{ Entered "
<< *theFileName << ":"
<< theLineNumber << " "
<< *theFunctionName << "()";
if(thisPtr > 0)
{
std::cout<<" this = "<< thisPtr;
}
std::cout<< std::endl;
layer++;
startTimeList.push_back(clock());
}
if(traceOn >= 0)
{
active = traceOn;
if (active && !previouslyActive)
{
std::cout<<"+++] Trace: Enabled"<<std::endl;
}
else if (previouslyActive && !active)
{
std::cout<<"+++[ Trace: Disabled"<<std::endl;
}
}
}
Trace::~Trace()
{
if (previouslyActive && !active)
{
std::cout<<"+++] Trace: Enabled"<<std::endl;
}
else if (active && !previouslyActive)
{
std::cout<<"+++[ Trace: Disabled"<<std::endl;
}
active = previouslyActive;
if (active || theFunctionName)
{
layer--;
double startTime = static_cast<double>(startTimeList.back());
startTimeList.pop_back();
double endTime = static_cast<double>(clock());
double elapsedTime = (endTime-startTime)/CLOCKS_PER_SEC;
std::cout<<"<--- Trace: ";
for( int L = 0; L < layer; ++L )
{
std::cout<<" ";
}
std::cout<<"} Leaving "
<< *theFileName << ":"
<< theLineNumber <<" "
<< *theFunctionName <<"()";
if(theThisPtr > 0)
{
std::cout<<" this = "<< theThisPtr;
}
std::cout<<" elapsedTime = "<<elapsedTime<<"s";
std::cout<< std::endl;
delete theFunctionName;
delete theFileName;
}
}
#包括“Trace.h”
bool Trace::active=true;
int Trace::layer=0;
std::list Trace::startTimeList;
跟踪::跟踪(常量字符*名称,
常量字符*文件,
常量整数行,
int-traceOn,//默认值:-1
const void*thisPtr)//默认值:0
:函数名(0),
文件名(0),
线号(0),
本PTR(本PTR),
先前活动(活动)
{
如果(活动)
{
函数名=新的std::string(名称);
theFileName=newstd::string(文件);
线号=线;
std::coutMay我想问一下您想要完成什么?可能有比日志语句更有用的东西。这样的程序通常也需要解析语法(这不是直接编写的)…例如,下面的字符串定义char greeting[]=“void SomeFunc1”;
可能会让一个简单的解析器误以为它是一个真正函数的开始。注释也可能包含字符串,这会让解析器感到困惑,所以它也需要解析注释…@HåkonHægland是对的,没有C解析器,您无法可靠地完成这项工作。shell脚本如何知道它看到void SomeFunc2(…){ <代码> >它是否在C样式注释分隔符内,例如,调试像这样的打印语句是个坏主意。但是如果您要这样做,请考虑诸如“代码>打印”(“%s开始\n”)、“{函数”)。
使用预处理器通过\uuuuu func\uuuuu
获取函数名,然后定义宏打印,可以取消定义以删除打印语句。除了修改代码,还有什么呢?虽然我没有使用它们,但似乎有可视化工具可供使用,以使其密集格式更易于理解。sh首先,在快速搜索中,谢谢。EdMorton。我删除了领先的代码>代码> >。我想你是说这个规则适用于C预处理器,对吗?你的答案不解决在子目录中修改1000个C++源文件的问题。@ MytInu:YUP,如我的“答案”中所述:“这只会稍微简化您的shell脚本需要。“因此,不,它实际上没有回答问题。我认为它重新引导问题,以寻求更好的最终结果。通常情况下,我认为这类事情应该是注释而不是答案,但我肯定不能将所有代码都放入注释中。你的答案主要只是一条注释,大意是“如果你的1000个C++代码文件是这样写的,你就不会有问题了。”他们说,事后聪明通常是20/20个……马蒂诺:实际上,我的回答是一个评论:“你将尝试用一个类似AWK的脚本做一些事情,我认为你应该用它做些别的事情。”“我的意思是,提出问题的人应该添加一个宏,而不是一对打印语句。这与现有代码无关。它所做的是使awk任务变得更简单(每个函数体只添加一个单词)还有一个更有用的最终结果。这将处理注释行,如猫躲避狗(和狼)
就好像它是一个函数定义,而为函数返回类型识别关键字的方法永远不适用于一般代码,因为函数可以返回用户定义的类型。此外,您不需要说$0~/void/
,只要/void/
是
$ awk -f tst.awk file
//SomeSrcFile.cpp
//Sample
ReturnType SomeClass::SomeFunc1(InputParameters)
{
char FuncName[] = "SomeClass::SomeFunc1()";
printf("%s begins\n", FuncName);
......
printf("%s ends\n", FuncName);
}
ReturnType SomeClass::SomeFunc2(InputParameters) {
char FuncName[] = "SomeClass::SomeFunc2()";
printf("%s begins\n", FuncName);
......
printf("%s ends\n", FuncName);
}
$ cat tst2.awk
BEGIN {
beg = "\tchar FuncName[] = \"%s()\";\n\tprintf(\"%%s begins\\n\", FuncName);\n"
end = "\n\tprintf(\"%%s ends\\n\", FuncName);"
}
/^[[:space:]]*[[:alnum:]_]+[[:space:]]+([[:alnum:]_]+::[[:alnum:]_]+)[(][^)]*[)]/ {
funcName = $0
gsub(/^[[:space:]]*[[:alnum:]_]+[[:space:]]+|[(][^)]*[)].*/,"",funcName)
}
/{/ && (++braceCnt == 1) { $0 = $0 ORS sprintf(beg,funcName) }
/}/ && (--braceCnt == 0) { $0 = sprintf(end,funcName) ORS $0 }
{ print }
// File: Trace.h
class Trace
{
public:
// print function entry message
Trace(const char *name,
const char *file,
const int line,
int traceOn = -1, // used for enabling/disabling Trace printing
// within a particular call chain.
const void* thisPtr = 0);
// print function exit message
~Trace();
static bool active;
private:
std::string* theFunctionName;
std::string* theFileName;
int theLineNumber;
const void* theThisPtr;
static int layer; // used for printout indenting
static std::list<clock_t> startTimeList; // used for printing timing information
bool previouslyActive;
};
#ifdef TRACE_ENABLED // define the real macros
#undef TRACE_ENABLED // must be re #defined preceding each '#include "Trace.h" '
#define TRACE__ Trace tr_( __FUNCTION__ , __FILE__ , __LINE__, -1, this );
#define TRACE_OFF__ Trace tr_off_(__FUNCTION__ , __FILE__ , __LINE__, false);
#define TRACE_ON__ Trace tr_on_( __FUNCTION__ , __FILE__ , __LINE__, true);
#else // DUMMY MACROS: if TRACE_ENABLED is not defined.
#define TRACE__
#define TRACE_ON__
#define TRACE_OFF__
#endif
//SomeSrcFile.cpp
//Sample
#define TRACE_ENABLED // comment this out to disable tracing in this file.
#include "Trace.h"
ReturnType SomeClass::SomeFunc1(InputParameters)
{
TRACE__
......
}
ReturnType SomeClass::SomeFunc2(InputParameters) {
TRACE__
......
}
#include "Trace.h"
bool Trace::active = true;
int Trace::layer = 0;
std::list<clock_t> Trace::startTimeList;
Trace::Trace(const char *name,
const char *file,
const int line,
int traceOn, // default: -1
const void* thisPtr) // default: 0
: theFunctionName(0),
theFileName(0),
theLineNumber(0),
theThisPtr(thisPtr),
previouslyActive(active)
{
if (active)
{
theFunctionName = new std::string(name);
theFileName = new std::string(file);
theLineNumber = line;
std::cout<<"---> Trace: ";
for( int L = 0; L < layer; ++L )
{
std::cout<<" ";
}
std::cout
<<"{ Entered "
<< *theFileName << ":"
<< theLineNumber << " "
<< *theFunctionName << "()";
if(thisPtr > 0)
{
std::cout<<" this = "<< thisPtr;
}
std::cout<< std::endl;
layer++;
startTimeList.push_back(clock());
}
if(traceOn >= 0)
{
active = traceOn;
if (active && !previouslyActive)
{
std::cout<<"+++] Trace: Enabled"<<std::endl;
}
else if (previouslyActive && !active)
{
std::cout<<"+++[ Trace: Disabled"<<std::endl;
}
}
}
Trace::~Trace()
{
if (previouslyActive && !active)
{
std::cout<<"+++] Trace: Enabled"<<std::endl;
}
else if (active && !previouslyActive)
{
std::cout<<"+++[ Trace: Disabled"<<std::endl;
}
active = previouslyActive;
if (active || theFunctionName)
{
layer--;
double startTime = static_cast<double>(startTimeList.back());
startTimeList.pop_back();
double endTime = static_cast<double>(clock());
double elapsedTime = (endTime-startTime)/CLOCKS_PER_SEC;
std::cout<<"<--- Trace: ";
for( int L = 0; L < layer; ++L )
{
std::cout<<" ";
}
std::cout<<"} Leaving "
<< *theFileName << ":"
<< theLineNumber <<" "
<< *theFunctionName <<"()";
if(theThisPtr > 0)
{
std::cout<<" this = "<< theThisPtr;
}
std::cout<<" elapsedTime = "<<elapsedTime<<"s";
std::cout<< std::endl;
delete theFunctionName;
delete theFileName;
}
}