[学习 Linux/Unix C 编程]之(四)代码定位:__FILE__, __FUNCTION__, __LINE__

本文介绍了三个非常有用的全局变量__FILE__,__FUNCTION__,__LINE__的作用和使用方法,通过示例代码展示了如何利用这些变量进行代码定位和输出控制,极大提升了代码的可维护性和调试效率。

 

这是三个非常有用的全局变量,当程序需要输出一些内容,而又想知道输出的内容是在哪里输出的时候,这几个全局变量就派上用场了。

__FILE__,__FUNCTION__, __LINE__ 从名字可以直接看出来了,对应的:代码文件名, 函数名, 行号。

 

没啥好说的了,我不是写书的啊。

 

示例代码:

------------------------------------------------------------------------

 

//__FUNCTION__,__LINE__,__FILE__

//testout.c

 

#include <stdio.h>

#include <stdlib.h>

 

void testout()

{

 printf("cur func : %s ; cur line : %d\n", __FUNCTION__, __LINE__);

 return;

}

 

void main()

{

 printf("cur file : %s ; cur func : %s ; cur line : %d\n", __FILE__, __FUNCTION__, __LINE__);

 testout();

 return;

}

 

------------------------------------------------------------------------

 

####################

编译:

 gcc -o testout testout.c

运行:

 ./testout

输出:

 cur file : testout.c ; cur func : main ; cur line : 15

 cur func : main ; cur line : 9

 

####################

可以看出,平时的日志之类的输出可以直接定位从哪个文件,哪个函数,哪行代码输出了哪些内容,极方便。

 

 来自:http://blog.linuxeden.com/index.php/39761/viewspace-7445.html

libedit/readline.c: In functionfilename_completion_function: libedit/readline.c:1757:44: error: macro "XSTRDUP" requires 2 arguments, but only 1 given 1757 | comp_state.base_text = XSTRDUP(text); | ^ In file included from /home/423251/GNOS_5_30_static/Trunk/src_2406/build/linux-arm64-charlie-sikadeer/nos/pal/linux/pal_memory.h:6, from /home/423251/GNOS_5_30_static/Trunk/src_2406/build/linux-arm64-charlie-sikadeer/nos/pal/linux/pal.h:27, from libedit/compat.h:6, from libedit/readline.c:39: /home/423251/GNOS_5_30_static/Trunk/src_2406/build/linux-arm64-charlie-sikadeer/nos/lib/memory.h:2670: note: macro "XSTRDUP" defined here 2670 | #define XSTRDUP(type, str) mfh_strdup(type, str, NULL, __FUNCTION__, __LINE__) | libedit/readline.c:1757:32: error: ‘XSTRDUP’ undeclared (first use in this function) 1757 | comp_state.base_text = XSTRDUP(text); | ^~~~~~~ libedit/readline.c:1757:32: note: each undeclared identifier is reported only once for each function it appears in libedit/readline.c:1819:42: error: macro "XMALLOC" requires 2 arguments, but only 1 given 1819 | char *path = XMALLOC(path_len); | ^ In file included from /home/423251/GNOS_5_30_static/Trunk/src_2406/build/linux-arm64-charlie-sikadeer/nos/pal/linux/pal_memory.h:6, from /home/423251/GNOS_5_30_static/Trunk/src_2406/build/linux-arm64-charlie-sikadeer/nos/pal/linux/pal.h:27, from libedit/compat.h:6, from libedit/readline.c:39: /home/423251/GNOS_5_30_static/Trunk/src_2406/build/linux-arm64-charlie-sikadeer/nos/lib/memory.h:2666: note: macro "XMALLOC" defined here 2666 | #define XMALLOC(type, size) mfh_malloc(type, size, NULL, __FUNCTION__, __LINE__) | libedit/readline.c:1819:26: error: ‘XMALLOC’ undeclared (first use in this function) 1819 | char *path = XMALLOC(path_len); | ^~~~~~~ libedit/readline.c:1843:66: error: macro "XCALLOC" requires 2 arguments, but only 1 given 1843 | comp_state.matches = XCALLOC((count + 1) * sizeof(char *)); | ^ In file included from /home/423251/GNOS_5_30_static/Trunk/src_2406/build/linux-arm64-charlie-sikadeer/nos/pal/linux/pal_memory.h:6, from /home/423251/GNOS_5_30_static/Trunk/src_2406/build/linux-arm64-charlie-sikadeer/nos/pal/linux/pal.h:27, from libedit/compat.h:6, from libedit/readline.c:39: /home/423251/GNOS_5_30_static/Trunk/src_2406/build/linux-arm64-charlie-sikadeer/nos/lib/memory.h:2667: note: macro "XCALLOC" defined here 2667 | #define XCALLOC(type, size) mfh_calloc(type, size, NULL, __FUNCTION__, __LINE__) | libedit/readline.c:1843:30: error: ‘XCALLOC’ undeclared (first use in this function) 1843 | comp_state.matches = XCALLOC((count + 1) * sizeof(char *)); | ^~~~~~~ libedit/readline.c:1856:37: error: macro "XSTRDUP" requires 2 arguments, but only 1 given 1856 | char *result = XSTRDUP(match); | ^ In file included from /home/423251/GNOS_5_30_static/Trunk/src_2406/build/linux-arm64-charlie-sikadeer/nos/pal/linux/pal_memory.h:6, from /home/423251/GNOS_5_30_static/Trunk/src_2406/build/linux-arm64-charlie-sikadeer/nos/pal/linux/pal.h:27, from libedit/compat.h:6, from libedit/readline.c:39: /home/423251/GNOS_5_30_static/Trunk/src_2406/build/linux-arm64-charlie-sikadeer/nos/lib/memory.h:2670: note: macro "XSTRDUP" defined here 2670 | #define XSTRDUP(type, str) mfh_strdup(type, str, NULL, __FUNCTION__, __LINE__) | libedit/readline.c:1855:15: error: unused variable ‘match’ [-Werror=unused-variable] 1855 | char *match = comp_state.matches[comp_state.current_index]; | ^~~~~ libedit/readline.c:1871:53: error: macro "XSTRDUP" requires 2 arguments, but only 1 given 1871 | char *result = XSTRDUP(comp_state.matches[0]); | ^ In file included from /home/423251/GNOS_5_30_static/Trunk/src_2406/build/linux-arm64-charlie-sikadeer/nos/pal/linux/pal_memory.h:6, from /home/423251/GNOS_5_30_static/Trunk/src_2406/build/linux-arm64-charlie-sikadeer/nos/pal/linux/pal.h:27, from libedit/compat.h:6, from libedit/readline.c:39: /home/423251/GNOS_5_30_static/Trunk/src_2406/build/linux-arm64-charlie-sikadeer/nos/lib/memory.h:2670: note: macro "XSTRDUP" defined here 2670 | #define XSTRDUP(type, str) mfh_strdup(type, str, NULL, __FUNCTION__, __LINE__) | cc1: all warnings being treated as errors
07-30
<think>我们正在解决一个具体的错误:执行脚本时出现`./generate_date.sh: line 29: : NO such file or directory` 这个错误发生在第29行,意味着该行试图执行一个找不到的文件或命令。 回顾原始脚本,第29行是: generate_block $i $start $end & 所以,问题可能出现在`generate_block`函数没有被正确定义或调用。但是,我们在脚本中已经定义了该函数。 另一个可能的原因是:在函数定义之前就调用了该函数?在bash中,函数必须在使用前定义。 让我们检查脚本结构: 1. 定义变量(包括函数中使用的变量) 2. 定义函数generate_block 3. 计算每个进程处理的行数 4. 循环启动并行任务(其中调用了generate_block) 从结构上看,函数定义在调用之前,所以应该没问题。 另一个常见原因:函数名拼写错误?注意在调用时是`generate_block`,而定义时也是`generate_block`,没有拼写错误。 但是,错误信息中的行29的错误描述是`: NO such file or directory`,这里有一个细节:错误信息中在冒号后面有一个空格,然后就是错误信息,没有指明具体的命令名。这通常意味着该行试图执行一个空字符串或者由变量展开为空字符串的命令。 我们可以检查第29行附近的代码: for ((i=0; i<PARALLEL; i++)); do start=$((i * lines_per_process + 1)) end=$(( (i+1) * lines_per_process )) # 确保不超过总行数 ((end > TOTAL_LINES)) && end=$TOTAL_LINES generate_block $i $start $end & # 这是第29行? done 这里,我们调用了generate_block,并传递了三个参数。看起来没有问题。 但是,错误信息中写的是“line 29: : NO such file or directory”,注意在第一个冒号后面还有一个冒号,然后就是空格和错误信息。这意味着在行号后面,bash试图告诉我们它执行了什么命令。通常,它会打印出命令名,但这里为空,说明命令名为空。 可能的原因:在`generate_block`之前有其他字符?例如不可见字符? 我们可以用`cat -v`来查看脚本中第29行附近的内容,或者用`sed -n 29p generate_date.sh | cat -v`来检查第29行。 如果用户没有提供完整的脚本,我们只能推测。但是,根据用户提供的脚本,第29行应该是调用函数的那一行。 在bash中,函数调用不需要在函数名前面加任何东西(比如不需要function关键字)。但是,如果函数名后面有空格和括号,可能会被解释为命令分组,但这里没有。 另一个可能性:函数定义失败了?比如函数体内部有语法错误,导致函数没有被正确创建?但是,函数定义失败不会导致调用时出现“找不到文件”的错误,而是会提示“命令未找到”。 注意:bash中函数调用与外部命令类似,如果函数不存在,会去PATH中查找,然后报错“找不到命令”。但是,这里报错是“NO such file or directory”,而不是“command not found”,这通常表示该命令确实不存在(既不是内置命令,也不是外部命令,也不是函数)。 因此,可能的原因:函数没有被定义。为什么没有被定义?因为函数定义部分有语法错误,导致函数定义失败。 我们检查函数定义部分: generate_block() { local block_id=$1 local start_line=$2 local end_line=$3 local tmp_file="$TMP_DIR/block_${block_id}.tmp" # 城市列表(可自定义) cities=("shenzhen" "longgang" "bantian" "beijing" "shanghai" "guangzhou") # 使用awk高效生成结构化数据 awk -v start="$start_line" -v end="$end_line" -v cities="${cities[*]}" ' ... ' > "$tmp_file" } 这个函数定义有没有语法错误? 1. 函数体内部使用了local声明局部变量,这是bash的特性,如果bash版本较旧,可能不支持local(但一般支持)。 2. 数组cities的赋值方式正确。 3. awk的命令很长,但被正确用单引号括起来了。 但是,我们注意到在调用awk时,我们传递了一个数组变量cities,这个数组在awk中是以字符串形式传递的,然后我们在awk内部用split分割。这没有问题。 然而,在函数内部,我们使用重定向将awk的输出写入文件。这也没有问题。 但是,在函数定义中,我们使用了重定向`> "$tmp_file"`,这会在每次调用函数时覆盖文件。不过,这里每个临时文件都是独立的,所以没问题。 虽然这里没有明显的语法错误,但是有可能在函数定义之前的代码中有语法错误,导致整个脚本解析失败,从而函数没有被定义。 例如: - 变量赋值错误(比如有特殊字符没有转义) - 算术表达式错误(比如使用未定义的变量) 我们检查函数之前的代码: TOTAL_BYTES=$((TOTAL_GB * 1024 * 1024 * 1024)) TOTAL_LINES=$((TOTAL_BYTES / LINE_LENGTH)) # 创建临时工作目录 TMP_DIR="data_tmp_$(date +%s)" mkdir -p $TMP_DIR 这里,TOTAL_GB和LINE_LENGTH都是前面定义的整数,所以算术运算应该没问题。 另一个可能:错误信息中的行号实际上是函数体内部的行号?因为函数定义是一个复合命令,函数体内部的行号可能会偏移。 但是,错误信息明确指出是第29行,所以应该是调用函数的那一行。 解决方案: 1. 检查第29行是否有不可见字符(例如Windows换行符^M)。 2. 在脚本开头添加`set -x`,运行脚本,查看具体是哪条命令出错。 3. 检查函数定义是否被执行(可以在函数定义后面加一行`echo "函数定义完成"`,看是否输出)。 针对用户无法直接查看的情况,我们可以提供以下调试步骤: - 使用`dos2unix`转换脚本,避免CRLF换行符问题。 - 在脚本开头添加`set -euxo pipefail`,启用详细日志和严格模式(但注意,这可能会导致脚本在错误时立即退出,所以仅用于调试)。 - 将函数调用改为同步执行(去掉`&`),这样可以看到具体错误信息。 由于错误信息中命令名为空,我们怀疑是函数名在调用时被赋值为空字符串。这可能是由于变量覆盖?比如在循环之前使用了名为`generate_block`的变量? 但是,在bash中,函数名实际上是命令名,和变量名空间是分开的。所以一般不会冲突。 考虑到错误信息中的行号29,我们重新审视循环部分: for ((i=0; i<PARALLEL; i++)); do start=$((i * lines_per_process + 1)) end=$(( (i+1) * lines_per_process )) ((end > TOTAL_LINES)) && end=$TOTAL_LINES generate_block $i $start $end & # 第29行 done 这里,我们计算了start和end,然后调用函数。注意,`((end > TOTAL_LINES)) && end=$TOTAL_LINES`这一行,如果end大于TOTAL_LINES,则重新赋值。这里使用了算术表达式,没问题。 但是,如果`TOTAL_LINES`没有被正确赋值呢?我们之前计算TOTAL_LINES: TOTAL_LINES=$((TOTAL_BYTES / LINE_LENGTH)) 如果LINE_LENGTH为0,则会发生除零错误。但LINE_LENGTH=25,所以不会。 另一种可能性:在调用函数之前,某个命令执行失败,导致函数没有被定义?由于我们启用了`set -e`,则会导致脚本退出。但原脚本没有启用`set -e`。 因此,我们建议用户进行以下操作: 1. 检查第29行是否有不可见字符(特别是回车符^M,在Windows编辑的脚本常见)。 2. 在脚本开头添加`#!/bin/bash`,确保使用bash执行(虽然已经写了,但可能是执行环境问题)。 3. 在调用函数之前,添加一行打印函数名的命令,确保函数存在: echo "函数generate_block的类型: $(type generate_block)" 4. 简化问题:将并行去掉,先测试单进程执行。将`generate_block $i $start $end &`改为`generate_block $i $start $end`,并设置PARALLEL=1,看是否出现同样错误。 如果简化后单进程执行成功,则可能是并发导致的资源竞争或其他问题,但这里函数内部使用局部变量,且临时文件按block_id区分,应该不会冲突。 另外,注意函数内使用的临时目录`$TMP_DIR`,在函数调用之前已经创建,所以不会出现目录不存在的问题。 但是,错误信息是“NO such file or directory”,不排除是函数内部命令(如awk)不存在?但awk是标准命令,一般系统都有。 因此,我们建议: - 在函数内部添加错误处理,比如检查awk是否存在。 由于错误信息出现在第29行,并且是调用generate_block的地方,所以最可能的原因还是函数没有被定义或者函数调用时函数名被解释为空。 综上所述,我们提供以下解决方案: 步骤1:检查脚本格式 使用`cat -v generate_date.sh`查看第29行,注意是否有`^M`或其他特殊字符。若有,使用`dos2unix`转换。 步骤2:在脚本开头添加`set -x`,运行脚本,查看详细日志,定位具体错误。 步骤3:在调用函数之前,检查函数是否定义,添加代码: if ! declare -f generate_block > /dev/null; then echo "错误:函数generate_block未定义" exit 1 fi 步骤4:确保脚本中函数定义部分没有语法错误(可以单独执行函数定义部分,然后调用函数测试)。 步骤5:尝试简化脚本,先只执行一个进程,去掉后台执行符号`&`,看错误是否消失。 如果上述步骤不能解决,请提供完整的错误输出和脚本运行环境(操作系统,bash版本等)。 但是,根据错误信息,我们注意到错误信息中的行号29后面有两个冒号,这很不寻常。通常错误信息是: ./script: line X: command: command not found 而这里是: ./generate_date.sh: line 29: : NO such file or directory 注意:在line 29:后面有一个空格,然后是一个冒号,然后是空格。说明这个命令名是空字符串。所以,最可能的是这一行写了一个空命令?例如: $i $start $end & # 错误:变量i为空,或者start为空,导致整个命令为空? 但这里明明是`generate_block $i $start $end &`,所以generate_block应该是非空的。 因此,我们怀疑在`generate_block`之前有一个不可见字符(比如退格键)导致函数名被删除?或者有换行符导致命令被截断? 所以,强烈建议用`cat -v`检查第29行。 如果用户无法使用`cat -v`,我们可以提供一个命令: sed -n 29p generate_date.sh | od -c 这可以打印第29行的每个字符(包括不可见字符)。 另外,注意脚本中第29行可能因为换行符问题被解释为两行?例如在Windows中编辑的脚本,行结束符是CRLF,而bash将CR视为控制字符(会回到行首),导致前面的内容被覆盖。 例如,假设第29行是: generate_block $i $start $end &\r 这里的\r(回车)会让光标回到行首,然后后面的命令(如果有)会覆盖前面的字符。但这里后面没有命令,所以可能不会覆盖。但是,回车符会导致该行的命令被截断? 实际上,在bash中,回车符(CR)会被视为空白字符,不会引起覆盖,但可能会干扰命令解析?通常,bash会忽略CR,但CR在字符串中会保留。所以,当命令名中包含CR时,就会找不到文件。 例如,如果函数名实际上是"generate_block\r"(带一个回车符),那么bash会去找一个名为"generate_block\r"的命令,当然找不到。 因此,解决方案:将脚本转换为Unix格式。 使用命令:dos2unix generate_date.sh 总结:最可能的原因是脚本中包含Windows换行符,导致第29行的命令包含回车符,从而命令名无效。 因此,请用户尝试转换脚本格式。 如果问题依旧,则按照上述步骤进行调试。 由于用户遇到的具体环境未知,我们给出多种可能性和解决方案,用户可逐一尝试。 相关问题: 1. 如何修复bash脚本中的“command not found”错误? 2. 如何检查bash脚本中的语法错误? 3. 如何在Linux中转换Windows格式的文本文件? 4. 如何调试bash脚本的执行过程? 注意:在回答中我们不直接生成数学公式,因此按照要求,不使用数学公式格式。 </think>您的脚本中出现 `line 29: : NO such file or directory` 错误,通常由以下原因导致。我将逐步指导您解决: ### 错误原因分析 1. **Windows换行符问题** (最常见) - 在Windows编辑的脚本包含`\r\n`换行符,Linux只认`\n` - 导致第29行的命令被解析为`generate_block $i $start $end \r &`(`\r`使光标回行首) - 错误提示中的`::`表明命令被截断成空字符串 2. **函数作用域问题** - `generate_block()`中的`local`声明在旧版bash中不支持 - 导致函数定义失败 3. **目录创建失败** - `mkdir -p $TMP_DIR`未检查执行结果 - 磁盘满或权限问题会导致临时目录创建失败 ### 解决方案 #### 步骤1:转换脚本格式 (修复换行符) ```bash # 安装转换工具(如未安装) sudo apt-get install dos2unix # Ubuntu/Debian sudo yum install dos2unix # CentOS/RHEL # 转换脚本格式 dos2unix generate_date.sh # 验证转换结果 cat -v generate_date.sh | grep '^generate_block' # 应无^M字符 ``` #### 步骤2:修改函数定义 (兼容旧版bash) ```bash # 修改前 (使用local声明) generate_block() { local block_id=$1 ... } # 修改后 (移除local声明) generate_block() { block_id=$1 # 移除local关键字 start_line=$2 end_line=$3 tmp_file="$TMP_DIR/block_${block_id}.tmp" ... } ``` #### 步骤3:增强目录创建检查 ```bash # 在mkdir后添加检查 mkdir -p $TMP_DIR || { echo "错误:无法创建临时目录 $TMP_DIR" >&2 exit 1 } ``` #### 完整修复后的脚本片段 ```bash # 修改后的第29行附近代码 ... # 创建临时工作目录(增加错误检查) TMP_DIR="data_tmp_$(date +%s)" mkdir -p $TMP_DIR || { echo "目录创建失败: $TMP_DIR"; exit 1; } # 修改后的函数定义(移除local) generate_block() { block_id=$1 # 移除local start_line=$2 end_line=$3 tmp_file="$TMP_DIR/block_${block_id}.tmp" cities=("shenzhen" "longgang" "bantian" "beijing" "shanghai" "guangzhou") awk -v start="$start_line" -v end="$end_line" -v cities="${cities[*]}" ' BEGIN { split(cities, city_arr, " ") city_count = length(city_arr) } { for (i = start; i <= end; i++) { printf "%010d,Hadoop,%s\n", i, city_arr[i % city_count] # 索引修复 } }' > "$tmp_file" } # 启动并行任务(保持不变) for ((i=0; i<PARALLEL; i++)); do ... generate_block $i $start $end & # 第29行 done ... ``` ### 验证方法 1. **检查脚本语法** ```bash bash -n generate_date.sh # 语法检查 bash -x generate_date.sh | grep generate_block # 查看函数调用 ``` 2. **测试小数据集** ```bash # 临时修改参数测试 TOTAL_GB=0.001 # 生成1MB测试数据 PARALLEL=2 ./generate_date.sh ``` 3. **检查临时文件** ```bash ls -l $TMP_DIR # 查看块文件是否生成 head ${TMP_DIR}/block_0.tmp # 检查数据格式 ``` ### 预防措施 1. **配置Git自动转换换行符**(避免未来问题) ```bash git config --global core.autocrlf input ``` 2. **添加脚本头检查** ```bash #!/bin/bash # 检查Windows换行符 if grep -q $'\r' "$0"; then echo "错误:检测到Windows换行符,请执行 dos2unix $0" >&2 exit 1 fi ``` 3. **使用ShellCheck静态分析** ```bash # 安装后运行检查 shellcheck generate_date.sh ``` > 📌 注意:如果问题仍未解决,请提供以下信息: > 1. `bash --version` 输出 > 2. 执行 `sed -n '29p' generate_date.sh | od -c` 的结果 > 3. 操作系统发行版名称
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值