背景: 我用若干个不同的配置文件大同小异,只是几个参数不同。如果一个个手工去修改,非常繁琐。于是有下面的:
/*in-place replace mth line 's nth word with val, keeping spaces as original as possible.
not support tab, you may use:
sed -i 's/\t/ /g' inputfile
usage example:
find . -name config.mak -exec ./file-inplace {} 260 3 '2#' \;
if on linux, keep PC dos ending:
find . -name config.mak -exec unix2dos {} \;
author: ludi 2013.12
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char line[9192];
int word_len(char *w)
{
int len = 0;
/*while(!isspace(*w)){++w; ++len;} <-- can't handle non-ascii*/
for(;*w; ++w){
if(' ' == *w || '\n' == *w || '\t' == *w){break;}
++len;
}
return len;
}
int work(char *line, int n, char *val)
{
char *ptr = line;
int wlen, vlen, s = 0, cnt = 0;
for(; *ptr; ++ptr)
{
if(*ptr!= ' '){
s = 1;
}else if(1 == s){
++cnt;
s = 0;
}
if(cnt == n){
while(*ptr == ' ')++ptr;
wlen = word_len(ptr);
vlen = strlen(val);
if(vlen > wlen){
if(ptr[vlen] != ' ')memmove(ptr+vlen+1, ptr+wlen+1, strlen(ptr+wlen+1));
strncpy(ptr, val, vlen);
ptr[vlen] = ptr[vlen] ? ' ': '\n';
}else{
strncpy(ptr, val, vlen);
ptr += vlen;
while(*ptr != ' ' && *ptr != '\n' && *ptr != '\t')*ptr++ = ' ';
}
break;
}
}
}
int main(int ac, char **av)
{
int mth, nth;
char *val, *tmpname = "xx.tmp";
FILE *fp, *tp;
int i, ch;
if(ac < 5)return printf("usage: file-in-replace inputfile mth nth val\n");
fp = fopen(av[1], "r+");
tp = fopen(tmpname, "w+");
if(!fp||!tp){return printf("can't open %s", av[1]);}
mth = atoi(av[2]);
nth = atoi(av[3]);
val = av[4];
for(i = 1; ; ++i){
memset(line, 0, sizeof(line));
if(!fgets(line, sizeof(line), fp)){break;}
if(i == mth){
work(line, nth-1, val);
}
fprintf(tp, "%s", line);
}
rewind(fp);
rewind(tp);
/*while(!feof(tp))fputc(fgetc(tp), fp);*/
while((ch = fgetc(tp)) != EOF){
fputc(ch, fp);
}
fclose(fp);
fclose(tp);
remove(tmpname);
return 0;
}
最先我也尝试过gawk, sed, 发现gawk 不能保留空格,这影响源文件的对齐。于是抽象了一个line-in-replace, 用这样的方案:
FILE=def.txt NR=160 COL=2 VAL=259
TMP=$(head -n $NR $FILE | tail -n 1 | ./line-in-replace $COL $VAL)
LANG=C sed -i -e "$NR s/.*/$TMP/" $FILE
那个LANG是为了处理中文,必须有,不然sed不正常。
后来觉得这三行复制粘贴也太麻烦,于是就整出了上面的file-in-replace。
贴在这里是为了记录我遇到的问题,一遍以后查。