![替换具有循环结构的第一列的内容](https://linux22.com/image/15087/%E6%9B%BF%E6%8D%A2%E5%85%B7%E6%9C%89%E5%BE%AA%E7%8E%AF%E7%BB%93%E6%9E%84%E7%9A%84%E7%AC%AC%E4%B8%80%E5%88%97%E7%9A%84%E5%86%85%E5%AE%B9.png)
我有一个文件,其中第一列要更改,例如,我有以下文件(在原始文件中我有多个列,但将以下截断为 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