替换具有循环结构的第一列的内容

替换具有循环结构的第一列的内容

我有一个文件,其中第一列要更改,例如,我有以下文件(在原始文件中我有多个列,但将以下截断为 2 列,文件末尾可能有空行)。

测试.txt:

0 a
2 b  
3 c 
4 d
5 e


我需要将第一列的内容从 0 -> 2, 2 -> 3 , 3 -> 5 , 4 -> 0, 5 -> 4 更改,我的最终文件应该变成,

2 a
3 b
5 c
0 d
4 e


我尝试使用 awk 如下,

awk '$1=="0"{$1="2"} $1=="2"{$1="3"} $1=="3"{$1="5"} $1=="4"{$1="0"} $1=="5"{$1="4"};1' test.txt

但由于 awk 不会逐行读取并更改所需的内容,因此输出是,

4 a
4 b
4 c
0 d
4 e


任何人都可以帮助我转换为我需要的东西,python、sed 或 awk,或任何脚本工具都是受欢迎的。

答案1

AWK实际上是逐行读取的,只需next在每次赋值后打印后添加语句即可,因此其余代码将被跳过。

awk '$1=="0"{ $1="2"; print; next }
     $1=="2"{ $1="3"; print; next }
     $1=="3"{ $1="5"; print; next }
     $1=="4"{ $1="0"; print; next }
     $1=="5"{ $1="4"; print; next }1' infile

或者使用控制标志来代替:

awk '!s && $1=="0"{ $1="2"; s=1 }
     !s && $1=="2"{ $1="3"; s=1 }
     !s && $1=="3"{ $1="5"; s=1 }
     !s && $1=="4"{ $1="0"; s=1 }
     !s && $1=="5"{ $1="4"; s=1 }
                  { print; s=0  }' infile

但您也可以按照以下方式完成所有操作:

awk -F'( )' 'BEGIN{ split("2 1 3 5 0 4", map) }
$1!=""{ $1=($1+1 in map)?map[$1+1]:$1 }1' infile

split(string,arryName) 函数我们创建一个名为的数组,map其索引和值如下所示,该数组根据 FS 进行分割(默认为 Space/Tabs)

Index        Value
map[<0>+1] -->   2
map[<1>+1] -->   we choice it 1 so it will be unchanged for <1> --> 1
map[<2>+1] -->   3
map[<3>+1] -->   5
map[<4>+1] -->   0
map[<5>+1] -->   4

<#>角度内的数字是第一列中的值,并且由于 awk 中数组的索引从 1 而不是 0 开始,因此我们将列值加一,然后从映射数组中获取相应的值。


作为通用的解决方案(但我仍然会使用上面的解决方案,因为几乎用户定义的键/值是连续的,并且数组索引可以用作键,但是)如果不是这样,那么您可以:

awk -F'( )' 'BEGIN{ len=split("0 2 2 3 3 5 4 0 5 4", map) }
{ for(i=1; i<=len/2; i+=2 ) if($1==map[i]){ $1=map[i+1]; break} }1' infile

答案2

$ awk 'BEGIN{split("1 3 5 0 4",map); map[0]=2} {$1=map[$1]} 1' file
2 a
3 b
5 c
0 d
4 e



答案3

带壳:

while read -r idx rest; do
  case $idx in 
    0) idx=2 ;; 
    2) idx=3 ;; 
    3) idx=5 ;; 
    4) idx=0 ;; 
    5) idx=4 ;;
  esac 
  echo "$idx $rest"
done < test.txt

要将输出写回原始文件,请执行以下操作之一

tmp=$(mktemp)
while ... < test.txt > "$tmp" && mv "$tmp" test.txt

或安装moreutils

while ... < test.txt | sponge test.txt

或者perl:

perl -pe '
    BEGIN {%map = (0=>2, 2=>3, 3=>5, 4=>0, 5=>4)}
    s{(\d+)}{$map{$1} // $1}e
' test.txt

答案4

使用 GNU sed 并打开扩展正则表达式模式-E。我们首先隔离第一列并运行y///命令,该命令将transliterate根据方案调整模式空间(现在它只有第一列)。然后我们调用原始行并从中取出第二列并丢弃原始的第一列。

sed -e '
  /^\S\s/!b
  s//&\n/;h
  s/\n.*//
  y/02345/23504/
  G;s/\n.*\n//
' file

2 a
3 b
5 c
0 d
4 e

其他方法可以是:

perl -lpe '
  s/^\S\s/
      $& =~ tr[02345]
              {23504}r
  /ex;
' file

或者作为内衬:

perl -lpe 's|^\S\s|$& =~ tr/02345/23504/r|e' file
python3 -c 'import sys
ifile = sys.argv[1]
with open(ifile) as fh:
  for l in fh:
    l = l.strip()
    p = l.find(" ")
    if p == 1:
      f1,rest = l[:p],l[p:];print(f1.translate(f1.maketrans("02345","23504")),rest,sep="")
    else:print(l)
' file

awk -v u="02345" -v v="23504" '
  BEGIN {
    gsub(/./, "&" FS, u)
    gsub(/./, "&" FS, v)
    split(u, a)
    for (i=1; i<=split(v,b); i++)
      c[a[i]] = b[i]
  }
  $1 in c{$1=c[$1]}1
' file

相关内容