使用sed更新特定字段

时间:2019-01-10 03:46:58

标签: shell unix awk sed

我正在尝试使用Bourne Shell中的sed命令更新特定行上的特定字段。

让我们说我有一个文件TopScorer.txt

Player:Games:Goals:Assists
Salah:9:9:3
Kane:10:8:4

我需要更新玩家的第三列(目标),我尝试了此命令,除非“游戏”和“目标”具有相同的值,然后它更新了第一个命令,否则该命令才起作用

player="Salah"
NewGoals="10"
OldGoals=$(awk -F':' '$1=="'$player'"' TopScorer.txt | cut -d':' -f3)
sed -i '/^'$player'/ s/'$OldGoals'/'$NewGoals'/' TopScorer.txt

Output> Salah:10:9:3 instead of Salah:9:10:3

有什么解决办法吗?我应该使用定界符和$3==...来指定该字段吗? 我还尝试了第二次出现的选项/ 2,但对我来说这不是很方便。

5 个答案:

答案 0 :(得分:4)

您只能单独使用awk而不是sed进行此操作。另请注意,awk具有内部语法,可从shell导入变量。所以您的代码就变成了

awk -F: -v pl="$player" -v goals="$NewGoals" 
    'BEGIN { OFS = FS } $1 == pl { $3= goals }1' TopScorer.txt

-F:将输入定界符设置为:,涉及-v的部分将您的shell变量导入到awk的上下文中。 BEGIN { OFS = FS }将输出字段分隔符设置为与输入相同。然后,我们使用导入的变量进行匹配,并将$3更新为所需值。

要就地进行修改,请使用一个临时文件

awk -F: -v pl="$player" -v goals="$NewGoals" 
   'BEGIN { OFS = FS } $1 == pl { $3= goals }1' TopScorer.txt > tmpfile && mv tmpfile TopScorer.txt

答案 1 :(得分:2)

您可以使用Perl尝试

Param([string]$user="")

Get-ADUser -Filter {mail -like "$user"} |
    Unlock-ADAccount -PassThru |
    Sync-ADObject -Destination "AZUDCMO01"

或导出它们并在单引号内调用Perl一线工具

$ player="Salah"
$ NewGoals="10"
$ perl -F: -lane "\$F[2]=$NewGoals  if ( \$F[0] eq $player ) ; print join(':',@F) " TopScorer.txt
Player:Games:Goals:Assists
Salah:9:10:3
Kane:10:8:4
$

请注意,Perl具有-i开关,您可以就地进行替换,因此

$ export NewGoals="10"
$ export player="Salah"
$  perl -F: -lane '$F[2]=$ENV{NewGoals} if $F[0] eq $ENV{player} ; print join(":",@F) ' TopScorer.txt
Player:Games:Goals:Assists
Salah:9:10:3
Kane:10:8:4
$

答案 2 :(得分:2)

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

(player=Salah;newGoals=10;sed -i "/^$name/s/[^:]*/$newGoals/3" /tmp/file)

使用子外壳程序,以免污染当前的外壳程序(...)。使用sed和模式匹配将每个记录的第一字段与变量player匹配,并将匹配记录的第三字段替换为newGoals的内容。

P.S。如果在后续处理中需要变量,则不需要子外壳,即删除()

答案 3 :(得分:1)

这将起作用。

在sed的第一部分中,我尝试匹配用于对播放器进行数学运算的整行,并且使用 \(

来保留要保留的所有字段。

第二部分,我用一些常量以及 \ 1 的值和 \ 2

的值重建行
player="Salah"
NewGoals="10"
sed "s/^$player:\([^:]*\):[^:]*:\([^:]*\)\$/$player:\1:$NewGoals:\2/"

答案 4 :(得分:1)

能否请您尝试一次。这种方法的优势在于,我不是对目标进行硬编码的领域。该程序将在目标存在的任何位置(例如->第4或第5个字段)查找标题的字段,它将仅针对该特定列进行更改。

第一个解决方案: :当您需要更改所有出现的玩家姓名时,请使用以下命令。

NewGoals=10
awk -v newgoals="$NewGoals" 'BEGIN{FS=OFS=":"} FNR==1{for(i=1;i<=NF;i++){if($i=="Goals"){field=i}}} FNR>1{if($1=="Salah"){$field=newgoals}} 1' Input_file

第二个解决方案: :如果您只想将特定玩家的目标值更改为特定行,请尝试执行以下操作。

NewGoals=10
awk -v newgoals="$NewGoals" 'BEGIN{FS=OFS=":"} FNR==1{for(i=1;i<=NF;i++){if($i=="Goals"){field=i}}} FNR>1{if($1=="Salah" && FNR==2){$field=newgoals}} 1' Input_file

以上仅对第2行进行更改,您可以通过在第二种情况下更改FNR==2来进行更改,其中FNR引用awk中的行号。如果要将输出保存到Input_file本身,则可以在上述代码后附加> temp_file && mv temp_file Input_file