字符串替换来检测空变量?

字符串替换来检测空变量?

我有一个脚本可以遍历文件并执行一些操作字符串替换插入日期。

#!/bin/bash
f="/tmp/file.txt" # with .txt extension
timestamp="$(date +%H%M%S)"
echo "${f%%.*}-$timestamp.${f#*.}"

它提供以下输出,这是正确的。

/tmp/file-220304.txt

但如果文件没有扩展名,脚本就会中断。

#!/bin/bash
f="/tmp/file" # no extension
timestamp="$(date +%H%M%S)"
echo "${f%%.*}-$timestamp.${f#*.}"
$ ./test.sh
/tmp/file-220359./tmp/file

是不是可以使用类似if未定义的方法${f:-not found}来填充空白?f我不知道如何用下面的方法解决上述问题。

#!/bin/bash
f="/tmp/file" # no extension
timestamp="$(date +%H%M%S)"
echo "${f%%.*}-$timestamp.${f#*.:-not found}"

结果:

$ ./test.sh
/tmp/file-221420./tmp/file

答案1

您还会遇到/tmp/dir.d/file文件问题。

zsh

#! /bin/zsh -
f="/tmp/file.txt" # with .txt extension
timestamp=${(%):-%D{%H%M%S}}

set -o extendedglob
extension=${(M)f%.[^./]#}
print -r -- ${f%$extension}-$timestamp$extension

使用标准sh语法(也适用于 bash/zsh):

#! /bin/sh -
f="/tmp/file.txt" # with .txt extension
timestamp=$(date +%H%M%S)

case ${f##*/} in
  (*.*) printf '%s\n' "${f%.*}-$timestamp.${f##*.}";;
  (*)   printf '%s\n' "$f-$timestamp";;
esac

使用bash,您可以替换timestamp=$(date +%H%M%S)printf -v timestamp '%(%H%M%S)T'以避免运行该date命令。

答案2

如果您想要一个简单的解决方案,这里有一个有点不优雅但可行的解决方案的演示:

#!/bin/bash

# Test cases
FILE="/tmp/file"
# FILE="/tmp/file."
# FILE="/tmp/file.txt"
# FILE="/tmp/file.txt."

# TIMESTAMP="$(date +%H%M%S)"
printf -v TIMESTAMP '%(%H%M%S)T'

echo "${FILE%%.*}-${TIMESTAMP}.$([[ $FILE =~ \..*$ ]] && { echo "${FILE#*.}"; } || { echo "txt"; })"

.txt如果缺少扩展名,上面的内容会添加到文件名中。

如果您不希望在缺少扩展名的情况下将默认扩展名添加到文件名中,请使用以下一行代码。

echo "${FILE%%.*}-${TIMESTAMP}$([[ $FILE =~ \..*$ ]] && { echo ".${FILE#*.}"; })"

答案3

为了简单起见

#!/bin/bash
f="/tmp/file"
timestamp="$(date +%H%M%S)"
out="${f}-${timestamp}";
[ "${f%.*}" != "${f##*.}" ] && out="${f%.*}-${timestamp}.${f##*.}";
echo "$out"

使其.在路径中安全

dir=${f%/*};
file=${f##*/};
name=${file%.*};
suffix=${file##*.};
[ "$dir" != "$file" ] && dir=${dir}/ || unset dir;
[ "$name" != "$suffix" ] && suffix=.${suffix} || unset suffix;
echo "${dir}${name}-${timestamp}${suffix}"

如果您需要在一行中删除换行符,
请注意:我建议使用完整时间戳(日期+时间)以确保它是唯一的

答案4

并不是没有f定义,而是在第一个点被删除之前该部分${f#*.}的值。f如果没有点,则不会删除任何内容,与${f#*.}相同$f。另外,由于它匹配第一的./foo.txt点,您会遇到类似(它给出)的路径问题-181548./foo.txt

您需要测试是否${f#*.}等于$f或使用类似 的内容${f#${f%.*}},如果没有点分隔的后缀,它将变为空。对应的就是${f%.${f##*.}}.尽管您仍然会再次遇到问题./file

问题./file建议首先从路径中拆分文件名部分,然后从那里继续。

另一种方法是使用正则表达式。这适用于我测试的文件名:

#!/bin/bash
f=$1
timestamp="$(date +%H%M%S)"
re='^((.*/)?[^/.]+)(\.[^/]+)?$'
if [[ $f =~ $re ]]; then
    echo "${BASH_REMATCH[1]}-${timestamp}${BASH_REMATCH[3]}"
fi

那是(.*/)?——以斜杠结尾的可选路径;[^/.]+-- 文件名的初始部分;(\.[^/]+)?-- 可选扩展。

相关内容