在 shell 脚本程序中,如果转换后的文件名尚不存在,我需要将文件名转换为大写。在这种特殊情况下,我只需将基本名称更改为大写,保留扩展名(如果有)不变。
我解决这个问题的想法是首先分别提取基本名称和扩展名,使用
tr
命令将基本名称转换为大写,然后检查目录中是否存在更改的基本名称和扩展名。
如果它不存在,那么我将使用 mv 将原始文件名更改为大写基本名称。现在我认为这可以通过两种方式完成:首先使用expr
,其次使用cut
with .
(space-period-space) 作为分隔符。
如果我想用于expr
提取基本名称(例如从文件名 -python1.py
或phonelist
),那么我写了这个:
basefile=`expr "$filename" : '\(.*\)\.*.*' `
我也使用了\.*
那些没有任何扩展名的文件名,因为\.*
会忽略零次或多次出现的.
,但此表达式expr
无法正常工作。对于任何文件名,它都会按原样返回整个文件名。
谁能解释一下我哪里错了。另请建议我如何使用expr
仅从文件名中提取扩展名。
答案1
如果 shell 是bash
,则仅使用 bash 参数扩展:
file="aaa.bbb.dat"
name=${file%.*} # delete everything after last dot
ext=${file##*.} # delete everything up to last dot
upcase=${name^^*}.$ext # uppercase everything
echo "$upcase"
AAA.BBB.dat
尝试更困难的情况:
file="déjà vu . dat "
name=${file%.*} # delete everything after last dot
ext=${file##*.} # delete everything up to last dot
upcase=${name^^*}.$ext # uppercase everything
echo ":$upcase:"
给出:
:DÉJÀ VU . dat :
所以:
- 在使用结果之前不需要双引号
- 即使对于非 ASCII 字符,大写似乎也可以
答案2
当组延伸多远存在模糊性时,正则表达式引擎首先优先考虑最长的匹配。对于任何文件名,\(.*\)
匹配全名并\.*.*
匹配空字符串。
您将需要两种情况:带或不带扩展名。另请注意,如果文件名以 开头.
,则这不是扩展名的开头。
我不明白你为什么要使用expr
. Shell 参数操作更容易。
在转换为大写时,请注意tr
Linux 上的实现不支持非 ASCII 语言环境。它只进行字节操作。例如echo accentué | tr a-z A-Z
结果为ACCENTUé
, not ACCENTUÉ
。请改用 awk 等区域设置感知工具。在 bash 中,您可以使用${filename^^?}
,但在 sh 中不可用。确保您的脚本在文件名编码的正确区域设置中运行。
我假设文件名不包含目录部分。如果有,请先将其分开。
case $filename in
?*.*) # There is an extension
base="${filename%.*}"; ext=".${filename##*.}";;
*) # No extension
base="$filename"; ext="";;
esac
upcased_base="$(printf %s. %base | awk '$0 = toupper($0)')"
upcased="${upcased_base%.}$ext"
然后删除尾随.
部分,确保脚本正确处理文件名,并在扩展名之前添加换行符。如果没有这个,命令替换将删除尾随的换行符。如果您已经确保文件名不包含换行符,则不需要此操作。%s.
$upcased_base
答案3
这是一个完全awk
基于 - 的解决方案,您可以将以下行放入 shell 脚本中:
uppercasename="$(echo "$filename" | awk 'BEGIN{FS=OFS="."} NF==1{$1=toupper($1)} {for (i=1;i<NF;i++) $i=toupper($i)} 1')"
这将使用.
as 字段分隔符用于输入和输出并且,如果只找到一个字段,则将其转换为大写,在所有其他情况下,将除最后一个字段之外的所有字段都转换为大写。然后它打印结果(这就是 的含义1
,它是 的简写符号{print}
)。
如果您正在使用bash
,您可以去掉管道并将其声明为
uppercasename="$(awk 'BEGIN{FS=OFS="."} NF==1{$1=toupper($1)} {for (i=1;i<NF;i++) $i=toupper($i)} 1' <<< "$filename")"
使用此处字符串。
请注意,这是这样设计的,以便在文件名以 结尾的边界情况下.
(如 中)myfile.this.txt.
,它会将其视为“空但存在的后缀”并将其转换为MYFILE.THIS.TXT.
.另外,如果文件名开始如果带有.
且没有其他扩展名(如.myfile
),则会将其保留为小写。