linux 如何使用sed和date命令替换和格式化字符串中的日期?

cetgtptt  于 2023-05-28  发布在  Linux
关注(0)|答案(4)|浏览(235)

我有一个像"M;12345678900000;Z;;FEWO;F;010622;0.0;;;1.00;0.00;1.25;Y;;;XXX"这样的字符串,其中第七个字段是一个ddmmyy格式的日期。我需要用相同格式的日期替换此字段,但要早3天。
我尝试了以下操作:

echo "M;12345678900000;Z;;FEWO;F;010622;0.0;;;1.00;0.00;1.25;Y;;;XXX" | sed "s/;\([0-3][0-9][0-1][0-9]2[0-9]\);/;$(date -d "$(date -d \1 '+%d%m%y') - 3 days" '+%d%m%y');/"

问题是,我可以使用引用(\1)或脚本($(date..)),但我不能创建一个两者都能工作的工作示例。我想这是引号的问题,但我也无法正确地逃避它们。另外,像-E, -e, -r这样的sed开关也帮不了我。我希望看到一个工作的sed示例,但我也在考虑用awk解决这个问题。
任何帮助感激不尽。

2ul0zpep

2ul0zpep1#

使用awk:

awk '
BEGIN {
 FS = OFS = ";"
}
{
  dt = substr($7, 5, 2) substr($7, 3, 2) substr($7, 1, 2)
  cmd = "date -d \"" dt "-3days\" +%d%m%y"
  cmd | getline $7
  close(cmd)
  print
}'

当然,使用GNU awk可以更快。

gawk '
BEGIN {
  FS = OFS = ";"
  cmd = "stdbuf -oL date -f - +%d%m%y"
}
{
  dt = substr($7, 5, 2) substr($7, 3, 2) substr($7, 1, 2)
  print dt "-3days" |& cmd
  cmd |& getline $7
  print
}'
syqv5f0l

syqv5f0l2#

我发现dateutils对于日期/时间操作通常更方便,例如:使用来自coreutils的pastecut(假设数据来自infile):

paste -d';'                                                                 \
      <( <infile cut -d';' -f1-6 )                                          \
      <( <infile cut -d';' -f7 | dateadd -i '%d%m%y' -f '%d%m%y' -- '-3d' ) \
      <( <infile cut -d';' -f8- )
x6h2sr28

x6h2sr283#

使用shell和日期:date需要输入日期格式为mm/dd/yy,因此需要进行一些字符串操作。

s="M;12345678900000;Z;;FEWO;F;010622;0.0;;;1.00;0.00;1.25;Y;;;XXX"
# split the string
IFS=';' read -ra fields <<<"$s"
# reset the date
fields[5]=$(date -d "${fields[5]:2:2}/${fields[5]:0:2}/${fields[5]:4} - 3 days" '+%d%m%y')
# and join the fields
t=$(IFS=';'; echo "${fields[*]}")
echo "$t"
M;12345678900000;Z;FEWO;F;290522;0.0;1.00;0.00;1.25;Y;XXX=tp
                          ^^^^^^
5kgi1eie

5kgi1eie4#

awk可能是一个更好的候选人,但当你要求一个sed解决方案...
使用GNU sed,您可以使用shell执行成功替换的结果,并使用结果替换模式空间,这要归功于替换命令的e标志。使用GNU coreutils中的date实用程序的示例,如果您的行不包含date控件并且没有引号

$ printf 'M;12345678900000;Z;;FEWO;F;010622;0.0;;;1.00;0.00;1.25;Y;;;XXX' |
sed -E 's/(([^;]*;){6})(..)(..)(..)(.*)/date -d "\5\4\3 -3 days" +"\1%d%m%y\6"/e'
M;12345678900000;Z;;FEWO;F;290522;0.0;;;1.00;0.00;1.25;Y;;;XXX

这将用date命令替换输入行,以生成预期的输出。以你为例:

date -d "220601 -3 days" +"M;12345678900000;Z;;FEWO;F;%d%m%y;0.0;;;1.00;0.00;1.25;Y;;;XXX"

注意从日-月-年到年-月-日的重新排序。然后,由于e标志,它由shell执行。
如果这一行的其余部分可以被date解释为格式化命令,我们需要一些更复杂的东西:

$ printf 'M;12345678900000;Z;;FEWO;F;010622;0.0;;;1.00;0.00;1.25;Y;;;XXX' |
sed -E 'h
s/([^;]*;){6}(..)(..)(..).*/date -d "\4\3\2 -3 days" +%d%m%y/e
G;s/(.*)\n(([^;]*;){6}).{6}(.*)/\2\1\4/'
M;12345678900000;Z;;FEWO;F;290522;0.0;;;1.00;0.00;1.25;Y;;;XXX
  • 将包含输入行的模式空间复制到保持空间(h)。
  • 将整行替换为date -d "yymmdd -3 days" +%d%m%y,其中yymmdd是年-月-日格式的日期(在您的示例中为220601)。结果由shell执行,并且由于e标志,模式空间被结果替换。在您的示例中,它现在包含290522
  • 在模式空间(G)中添加一个换行符和保持空间。此时,模式空间包含(在您的示例中):
290522\nM;12345678900000;Z;;FEWO;F;010622;0.0;;;1.00;0.00;1.25;Y;;;XXX
  • 最后用你想要的最终格式替换图案空间。

相关问题