Bash 将2条awk或sed语句合并为一条并保存现有文件

Bash 将2条awk或sed语句合并为一条并保存现有文件,bash,date,awk,sed,Bash,Date,Awk,Sed,我的目标是只修改以数据开头的行,其中只有一行。在这一行中,我将替换51-80个字符和97-126个字符。我宁愿使用sed的一行程序,因为这样我就可以使用-I标志,然后它会根据需要保存文件 以下是我正在修改的文件中的行: data = '{\n"feature_name": "ALL",\n"start_date": "2020-06-07T08:34:00.000-06:00",\n"end_date": "2020-06-08T13:35:00.000-06:00",\n"product":

我的目标是只修改以数据开头的行,其中只有一行。在这一行中,我将替换51-80个字符和97-126个字符。我宁愿使用sed的一行程序,因为这样我就可以使用
-I
标志,然后它会根据需要保存文件

以下是我正在修改的文件中的行:

data = '{\n"feature_name": "ALL",\n"start_date": "2020-06-07T08:34:00.000-06:00",\n"end_date": "2020-06-08T13:35:00.000-06:00",\n"product": "SAEP",\n"limit":1000\n}'
下面是我的两条awk语句,它们正在修改字符串的开始日期和结束日期部分。我包括了两个变量,我用来计算开始日期的当前时间减去15分钟前的时间,以及结束日期的当前时间:

ctime=`date +%Y-%m-%dT%H:%M:%S.%3N-00:00`
fifteen_min_ago=`date -d "15 mins ago" +%Y-%m-%dT%H:%M:%S.%3N-00:00`

awk '$1 ~ /^data/{printf "%s%*s%s\n",substr($0,1,m-1),n-m,"'$fifteen_min_ago'",substr($0,n)}' m=51 n=80 file
awk '$1 ~ /^data/{printf "%s%*s%s\n",substr($0,1,m-1),n-m,"'$ctime'",substr($0,n)}' m=97 n=126 file
这工作得很好,但我需要保存文件。如果我运行两个单独的awk命令,它一次只修改一个。因此,要么我将它们组合起来并使用awk保存,要么需要一个sed one-liner,然后我可以使用
-I


谢谢。

您可以内联多行awk脚本,也可以将这两条语句放在一个文件中。为每个“过程”使用不同的变量名

awk '
$1 ~ /^data/ {
    $0 = sprintf("%s%*s%s\n",substr($0,1,m-1),n-m,"'$fifteen_min_ago'",substr($0,n))
    $0 = sprintf("%s%*s%s\n",substr($0,1,m2-1),n2-m2,"'$ctime'",substr($0,n2))
}
{ print }
' m=51 n=80 m2=97 n2=126 file > file.new &&
mv file.new
注意,有其他(更简单的)方法来实现问题中实施的he替换。这与问题中描述的方法最为相似

使用SED,更换更容易:

ctime=$(date +%Y-%m-%dT%H:%M:%S.%3N-00:00)
fifteen_min_ago=$(date -d "15 mins ago" +%Y-%m-%dT%H:%M:%S.%3N-00:00)

sed -e 's/"start_date": "[^"]*"/"start_date": "'$ctime'"/' \
    -e 's/"end_date": "[^"]*"/"end_date": "'$fifteen_min_ago'"/' < file > file.new && mv file.new file
ctime=$(日期+%Y-%m-%dT%H:%m:%S.%3N-00:00)
十五分钟以前=$(日期-d“十五分钟以前”+%Y-%m-%dT%H:%m:%S.%3N-00:00)
sed-e的/“开始日期”:“[^”]*”/“开始日期”:“$ctime”/”\
-e's/“结束日期”:“[^”]*”/“结束日期”:“$十五分钟前”/”file.new和&mv file.new文件

您可以内联多行awk脚本,也可以将这两条语句放在一个文件中。为每个“过程”使用不同的变量名

awk '
$1 ~ /^data/ {
    $0 = sprintf("%s%*s%s\n",substr($0,1,m-1),n-m,"'$fifteen_min_ago'",substr($0,n))
    $0 = sprintf("%s%*s%s\n",substr($0,1,m2-1),n2-m2,"'$ctime'",substr($0,n2))
}
{ print }
' m=51 n=80 m2=97 n2=126 file > file.new &&
mv file.new
注意,有其他(更简单的)方法来实现问题中实施的he替换。这与问题中描述的方法最为相似

使用SED,更换更容易:

ctime=$(date +%Y-%m-%dT%H:%M:%S.%3N-00:00)
fifteen_min_ago=$(date -d "15 mins ago" +%Y-%m-%dT%H:%M:%S.%3N-00:00)

sed -e 's/"start_date": "[^"]*"/"start_date": "'$ctime'"/' \
    -e 's/"end_date": "[^"]*"/"end_date": "'$fifteen_min_ago'"/' < file > file.new && mv file.new file
ctime=$(日期+%Y-%m-%dT%H:%m:%S.%3N-00:00)
十五分钟以前=$(日期-d“十五分钟以前”+%Y-%m-%dT%H:%m:%S.%3N-00:00)
sed-e的/“开始日期”:“[^”]*”/“开始日期”:“$ctime”/”\
-e's/“结束日期”:“[^”]*”/“结束日期”:“$十五分钟前”/”file.new和&mv file.new文件

可以将
sed
与OP的偏移量(
m
n
)结合使用,不过我们需要做一些数学运算,以获得正确的偏移量供
sed
使用

在本例中,我们将使用以下数据集:

$ cat xx
         1         2         3
data 6789012345678901234567890
abcdefghijklmnopqrstuvwxyz
我们将应用以下替换:

  • 在以
    ^data
    开头的行上
  • 将位置10-12替换为
    XXX
  • 将位置20-25替换为
    yyyyy
要通过单个
sed
调用实现这一点,我们需要2组偏移量:

m=10 ; n=12
o=20 ; p=25
r1=XXX
r2=YYYYYY
但在进入
sed
脚本之前,我们需要考虑:

  • 对于第一个
    sed
    模式匹配,我们希望保留位置1-9和13-EOL,并将从位置10开始的字符替换为长度为3的字符
  • 对于第二个
    sed
    模式匹配,我们希望保留位置1-19和26-EOL,并将从位置20开始的字符替换为长度为6的字符
我们可以获得我们的
sed
特定偏移量,如下所示:

m2=$((m-4-1))    # ( 10 - length('data') - 1 ) = 5 ; so length(data)=4 +  5 =  9 = end of first set of characters to keep
n2=$((n-m+1))    # ( 12 -             10 + 1 ) = 3
o2=$((o-4-1))    # ( 20 - length('data') - 1 ) = 15; so length(data)=4 + 15 = 19 = end of first set of characters to keep
p2=$((p-o+1))    # ( 25 -             20 + 1 ) = 6
现在,我们准备看看
sed
解决方案:

$ set -xv     # echo the `sed` command with all variables substituted with values
$ sed -E "s/(^data.{${m2}}).{${n2}}(.*)$/\1${r1}\2/g; s/(^data.{${o2}}).{${p2}}(.*)$/\1${r2}\2/g" xx
其中:

  • set-xv
    -允许我们调试后续的
    sed
    命令<代码>设置+xv将关闭
  • (^data.{${m2}})
    -对于以
    ^data
    开头的行,将前9个字符存储在缓冲区#1中;
    数据的长度
    +m2=5
  • {${n2}
    -匹配接下来的3个字符;将替换为
    r1的内容
  • (.*)$
    -匹配行的其余部分并存储在缓冲区#2中
  • \1${r1}\2
    -将行替换为buffer#1+
    ${r1}
    +buffer#2
  • (^data.{${o2}})
    -对于以
    ^data
    开头的行,将前19个字符存储在缓冲区#1中;
    数据的长度
    +o2=15
  • {${p2}
    -匹配接下来的6个字符;将替换为
    r2的内容
  • (.*)$
    -匹配行的其余部分并存储在缓冲区#2中
  • \1${r2}\2
    -将行替换为buffer#1+
    ${r2}
    +buffer#2
运行上述程序时,我们应获得:

+ sed -E 's/(^data.{5}).{3}(.*)$/\1XXX\2/g; s/(^data.{15}).{6}(.*)$/\1YYYYYY\2/g' xx
         1         2         3
data 6789XXX3456789YYYYYY67890
abcdefghijklmnopqrstuvwxyz
其中:

  • +sed
    开头的行向我们显示了实际的
    sed
    命令,所有变量都插入到混合中(这是事先运行
    set-xv
    的结果)
  • 输出的其余部分是带有所需字符串替换的输入文件
最后但并非最不重要的一点是,我们可以通过包含
-i
标志将所有这些写回原始文件:

$ set +xv      # turn off debugging
$ sed -i -E "s/(^data.{${m2}}).{${n2}}(.*)$/\1${r1}\2/g; s/(^data.{${o2}}).{${p2}}(.*)$/\1${r2}\2/g" xx
$ cat xx
         1         2         3
data 6789XXX3456789YYYYYY67890
abcdefghijklmnopqrstuvwxyz

可以将
sed
与OP的偏移量(
m
n
)结合使用,不过我们需要做一些数学运算,以获得正确的偏移量供
sed
使用

在本例中,我们将使用以下数据集:

$ cat xx
         1         2         3
data 6789012345678901234567890
abcdefghijklmnopqrstuvwxyz
我们将应用以下替换:

  • 在以
    ^data
    开头的行上
  • 将位置10-12替换为
    XXX
  • 将位置20-25替换为
    yyyyy
要通过单个
sed
调用实现这一点,我们需要2组偏移量:

m=10 ; n=12
o=20 ; p=25
r1=XXX
r2=YYYYYY
但在进入
sed
脚本之前,我们需要考虑:

  • 对于第一个
    sed
    模式匹配,我们希望保留位置1-9和13-EOL,并将从位置10开始的字符替换为长度为3的字符
  • 对于第二个
    sed
    模式匹配,我们希望保持位置1-19和26-EOL,并替换从@posi开始的字符
    awk -i inplace '
    BEGIN {
        ctime = strftime("%Y-%m-%dT%H:%M:%S.%3N-00:00")
        fifteen_min_ago = strftime("%Y-%m-%dT%H:%M:%S.%3N-00:00",systime()-(15*60))
    }
    match($0,/(^\s*data.*"start_date":[^"]*")([^"]+)(.*end_date":[^"]*")([^"]+)(.*)/,a) {
        $0 = a[1] fifteen_min_ago a[3] ctime a[5]
    }
    { print }
    ' file
    
    $ cat file
    data = '{\n"feature_name": "ALL",\n"start_date": "2020-06-07T08:34:00.000-06:00",\n"end_date": "2020-06-08T13:35:00.000-06:00",\n"product": "SAEP",\n"limit":1000\n}'
    
    $ awk -i inplace '
    BEGIN {
        ctime = strftime("%Y-%m-%dT%H:%M:%S.%3N-00:00")
        fifteen_min_ago = strftime("%Y-%m-%dT%H:%M:%S.%3N-00:00",systime()-(15*60))
    }
    match($0,/(^\s*data.*"start_date":[^"]*")([^"]+)(.*end_date":[^"]*")([^"]+)(.*)/,a) {
        $0 = a[1] fifteen_min_ago a[3] ctime a[5]
    }
    { print }
    ' file
    
    $ cat file
    data = '{\n"feature_name": "ALL",\n"start_date": "2020-06-13T13:56:46.3N-00:00",\n"end_date": "2020-06-13T14:11:46.3N-00:00",\n"product": "SAEP",\n"limit":1000\n}'