我可以更改 sed 的剪切命令吗?

Can I change a cut command for a sed?

我正在编写一个脚本,其中一部分是将日期值格式化为与 SQL*Plus 8 兼容的日期值。 日期格式为:

20191115103845

我需要将此字符串更改为

to_date('2019/11/15:10:38:45', 'yyyy/mm/dd:hh24:mi:ss')

我用的是剪切工具:

funcion(){
        data=
        year=$(echo $data| cut -c1,2,3,4)
        month=$(echo $data | cut -c5,6)
        day=$(echo $data | cut -c7,8)
        hour=$(echo $data| cut -c9,10)
        min=$(echo $data| cut -c11,12)
        sec=$(echo $date | cut -c13,14)
        echo "to_date('"$year"/"$month"/"$day":"$hour":"$min":"$sec"', 'yyyy/mm/dd:hh24:mi:ss')"
}

funcion $data_to_format

(data_to_format=20191115103845)

我的问题是这需要几秒钟的时间,我打算将此脚本用于超过 100 个文件,每个文件都有 100 多个日期。所以我认为 'sed' 命令可能对此更好。 但是我使用 ksh88(旧 ksh)并且 sed 没有 -E 选项并且无法使其工作。

谁能帮我解决旧的正则表达式 sed 解决方案?

您能否尝试在 GNU awk.

中使用显示的示例进行跟踪、编写和测试
awk -v s1="7" -v firstPart="to_date(" -v lastPart="yyyy/mm/dd:hh24:mi:ss" '
{
  print firstPart s1 substr([=10=],1,4) "/" substr([=10=],5,2) "/" substr([=10=],7,2)":"\
        substr([=10=],9,2)":"substr([=10=],11,2)":"substr([=10=],13,2) s1 ", " s1\
        lastPart s1 ")"
}
' Input_file

解释:

  • -v s1="7": 创建名为 s1 的变量,其中的值为 '
  • -v firstPart="to_date(": 创建变量 firstPart 其中包含字符串 to_date(
  • -v lastPart="yyyy/mm/dd:hh24:mi:ss": 创建其中包含字符串 yyyy/mm/dd:hh24:mi:ss 的变量 lastPart。
  • print: 使用打印函数打印变量和值。
  • firstPart s1 substr([=23=],1,4) "/" substr([=23=],5,2) "/" substr([=23=],7,2)":"\ substr([=23=],9,2)":"substr([=23=],11,2)":"substr([=23=],13,2) s1 ", " s1\ lastPart s1 ")":根据 OP 的要求在此处打印变量和子字符串。


第二个解决方案:这里也添加一个sed解决方案,这里使用sed的反向引用能力.

echo "20191115103845" |
sed 's/\(....\)\(..\)\(..\)\(..\)\(..\)\(..\)/to_date(\x27\/\/:::\x27, \x27yyyy\/mm\/dd:hh24:mi:ss\x27)/'

OR 感谢 tripleee 因为 \x27 可能在少数 sed 中不受支持所以添加另一种使用 [= 的方式16=] 里面 sed 代码。

echo "20191115103845" |
sed 's/\(....\)\(..\)\(..\)\(..\)\(..\)\(..\)/to_date('"'"'\/\/:::'"'"', '"'"'yyyy\/mm\/dd:hh24:mi:ss'"'"')/'

可能更好的方法是一直使用 shell 内置参数扩展。

没有一种简单的方法可以一步完成,但是您可以一次删除一个前缀,然后处理剩余的尾部。简而言之,${variable#pattern} returns $variable 删除了 pattern 上的任何前缀匹配,并且 ${variable%pattern} 对后缀匹配进行了相同的操作。

funcion(){
    data=${1#[0-9][0-9][0-9][0-9]}
    year=${1%"$data"}
    data=${data#"$year"}
    tail=${data#[0-9][0-9]}
    month=${data%"$tail"}
    data=${data#"$month"}
    tail=${data#[0-9][0-9]}
    day=${data%"$tail"}
    data=${data#"$day"}
    tail=${data#[0-9][0-9]}
    hour=${data%"$tail"}
    data=${data#"$hour"}
    tail=${data#[0-9][0-9]}
    min=${data%"$tail"}
    sec=${tail#"$min"}
    echo "to_date('"$year"/"$month"/"$day":"$hour":"$min":"$sec"', 'yyyy/mm/dd:hh24:mi:ss')"
}

这看起来很麻烦,但您应该会发现它比调用外部子进程(更不用说 6 个,就像在您的原始示例中那样)更快。

为了提高效率,如果您只是转换字符串,我不会为此使用任何外部工具,只需更改 shell 并使用 bash:

$ cat tst.sh
#!/usr/bin/env bash

funcion() {
    local date=
    if [[ "$date" =~ ^(....)(..)(..)(..)(..)(..)$ ]]; then
        printf "todate('%s/%s/%s:%s:%s:%s', 'yyyy/mm/dd/hh24:mi:ss')\n" \
            "${BASH_REMATCH[1]}" \
            "${BASH_REMATCH[2]}" \
            "${BASH_REMATCH[3]}" \
            "${BASH_REMATCH[4]}" \
            "${BASH_REMATCH[5]}" \
            "${BASH_REMATCH[6]}"
    fi
}

funcion '20191115103845'

$ ./tst.sh
todate('2019/11/15:10:38:45', 'yyyy/mm/dd/hh24:mi:ss')

如果您在循环中执行此操作,则将整个 shell 循环替换为对 awk 的一次调用。