ubuntu 如何使用shell脚本中的Sed命令替换文本文件中仅在一列中出现的所有值(列之间用;)?[副本]

o0lyfsai  于 2022-12-29  发布在  Shell
关注(0)|答案(3)|浏览(190)
    • 此问题在此处已有答案**:

sed: replace values in a single column(3个答案)
1小时前关闭。
我有一个文件,其中的列由一个半列(;),而我只想将某个特定列中出现的某个单词更改为另一个单词。列号根据保存该列号的变量进行区分。要更改的单词存储在变量中,要更改为的单词也存储在变量中。
我试过了

sed -i "s/\<$word\>/$wordUpdate/g" $anyFile

我试过这个,但它改变了整个文件中单词的所有出现!我只想在特定的列
列数存储在一个名为numColumn的变量中,列之间用半列分隔;

wrrgggsh

wrrgggsh1#

使用awk进行列编辑要简单得多,例如,如果您的输入如下所示:

68;61;83;27;60;70;84;11;46;62;93;97;40;23;19
33;70;17;49;81;21;68;83;16;6;42;38;68;81;89
73;40;95;64;32;33;77;56;23;11;70;28;33;80;24
8;9;74;6;86;78;87;41;11;79;23;28;71;99;15
29;87;77;9;98;12;7;66;60;85;20;14;55;97;17
39;24;21;58;23;61;39;26;57;70;76;16;70;53;8
37;46;18;64;56;28;86;7;80;71;94;46;19;53;43
71;2;47;62;9;21;68;9;9;80;32;59;73;74;72
20;34;89;58;74;92;86;35;48;81;50;6;63;67;90
78;17;6;63;61;65;75;31;33;82;24;5;90;46;12

您可以将c列中的60替换为s,如下所示:

<infile awk '$c ~ m { $c = s } 1' FS=';' OFS=';' c=5 m=60 s=XX

输出:

68;61;83;27;XX;70;84;11;46;62;93;97;40;23;19
33;70;17;49;81;21;68;83;16;6;42;38;68;81;89
73;40;95;64;32;33;77;56;23;11;70;28;33;80;24
8;9;74;6;86;78;87;41;11;79;23;28;71;99;15
29;87;77;9;98;12;7;66;60;85;20;14;55;97;17
39;24;21;58;23;61;39;26;57;70;76;16;70;53;8
37;46;18;64;56;28;86;7;80;71;94;46;19;53;43
71;2;47;62;9;21;68;9;9;80;32;59;73;74;72
20;34;89;58;74;92;86;35;48;81;50;6;63;67;90
78;17;6;63;61;65;75;31;33;82;24;5;90;46;12
uqjltbpv

uqjltbpv2#

这可能对您有用(GNU sed):

word=foo wordUpdate=bar numColumn=3
sed -i 'y/;/\n/
        s#.*#echo "&" | sed "'${numColumn}'s/\<'${word}'\>/'${wordUpdate}'/"#e
        y/\n/;/' file

将每一行转换为单独的文件,其中列是行。
用单词替换匹配的行(列号)以替换更新的单词。
反向转换。
注意:该解决方案依赖于GNU only e评估标志。此外,wordupdateWord可能需要引用。

nom7f22z

nom7f22z3#

这 * 可以 * 做一点创造力...
注意,我使用双引号来嵌入逻辑,这需要一点额外的注意,使你的\在反向引用时加倍。

$: word=baz; c=3; new=XX; lead="^([^;]*;){$((c-1))}"; sed -E "/$lead$word;/{s/($lead)$word/\\1$new/}" file
 1;2;3;4;5;6;7;8;9;0;
 foo;bar;XX;qux;foo;bar;baz;qux;
 a;b;c;d;e;f;g;

解释道:
lead="^([^;]*;){$((c-1))}"

  • ^表示 * 在记录的开头 *
  • (...)是指定重复的后续{...}的分组
  • [^;]*表示 * 零个或多个非分号 *
  • $((c-1))进行计算并返回比所需列小一的值;如果要查看第3列,它返回2。
    • SO ^([^;]*;){$((c-1))}位于记录的开头, 比列少一 * 出现的非分号后跟分号

因此,sed -E "/$lead$word;/{s/($lead)$word/\\1$new/}" file意味着读取file,并在所请求的列 * 中 * 出现$word的记录上,保存它之前的所有内容,并将该内容放回原处,但是用$new替换$word
即使您必须使用sed,我也推荐一个函数。

fix(){ 
  local word="$1" col="$2" new="$3" file="$4"
  local lead="^([^;]*;){$((col-1))}"
  sed -E "/$lead$word;/{s/($lead)$word/\\1$new/}" "$file"
}

使用中-

$: fix bar 2 HI file
1;2;3;4;5;6;7;8;9;0;
foo;HI;baz;qux;foo;bar;baz;qux;
a;b;c;d;e;f;g;

$: fix 1 1 XX file
XX;2;3;4;5;6;7;8;9;0;
foo;bar;baz;qux;foo;bar;baz;qux;
a;b;c;d;e;f;g;

$: fix bar 2 '(^_^)' file
1;2;3;4;5;6;7;8;9;0;
foo;(^_^);baz;qux;foo;bar;baz;qux;
a;b;c;d;e;f;g;

如果没有匹配则不做更改-

$: fix bar 5 HI file
1;2;3;4;5;6;7;8;9;0;
foo;bar;baz;qux;foo;bar;baz;qux;
a;b;c;d;e;f;g;

注-

如果您想匹配最后一个字段,此逻辑需要尾随分隔符-

$: fix 0 10 HI file
1;2;3;4;5;6;7;8;9;HI;
foo;bar;baz;qux;foo;bar;baz;qux;
a;b;c;d;e;f;g;

删除的分隔符:

$: fix 0 10 HI file
1;2;3;4;5;6;7;8;9;0
foo;bar;baz;qux;foo;bar;baz;qux
a;b;c;d;e;f;g

否则你就得把逻辑弄复杂一点。
但老实说,对于字段解析,使用awk,甚至perlpython,或者bash循环会更好,尽管这会相对较慢。

相关问题