在将变量传递给 grep 之前是否可以转义变量内字符串的所有元字符?我知道以前在 SE 上也有人问过类似的问题(这里)还有一个很好的解释这里,但我只是好奇是否可以使用基本/扩展 posix 正则表达式模式而不是 perl 模式? (目前我正在阅读 perl 正则表达式语法来首先理解它,而不是跳入解决方案)
为什么有这个要求:(元,不需要回答)
我试图编写一个用于拆分大文件的小脚本,其中我将文件拆分为file_name.ext.000
,file_name.ext.001
...等,效果很好。现在我不喜欢分割那些已经分割的文件(即文件名具有 3 个字符扩展名,全部是数字,并且它们的大小总和为原始文件大小。现在,如果我file_name.ext.*
也使用普通的 shell 扩展匹配文件file_name.ext.ext2
,因此即使不需要重新分割,总大小也会不匹配,因此我只会检查那些名称为数字的文件file_name.ext.###
,###
我当前查找这些部分的文件大小的表达式如下所示:
FILE_SIZE_EXISTING=$( (find "$DESTINATION" -type f -regextype posix-extended -regex "^$DESTINATION/$FILE_BASENAME(\.[[:digit:]]{3})?$" -print0 | xargs -0 stat --printf="%s\\n" 2>/dev/null || echo 0) | paste -sd+ | bc)
这适用于简单的文件名。但是,如果某些奇特的名称(例如包含 [ ] 等)它不起作用。有解决方法吗?我是 shell 脚本新手,因此不太了解 perl。
答案1
如何引用特殊字符(可移植)
以下代码片段在扩展正则表达式中的每个特殊字符之前添加一个反斜杠,用于用后跟该字符的反斜杠sed
替换任何出现的字符之一:][()\.^$?*+
raw_string='test[string]\.wibble'
quoted_string=$(printf %s "$raw_string" | sed 's/[][()\.^$?*+]/\\&/g')
$raw_string
这将删除;中的尾随换行符。如果这是一个问题,请通过在末尾添加一个惰性字符来确保字符串不以换行符结尾,然后删除该字符。
quoted_string=$(printf %sa "$raw_string" | sed 's/[][()\.^$?*+]/\\&/g')
quoted_string=${quoted_string%?}
如何引用特殊字符(在 bash 或 zsh 中)
Bash 和 zsh 具有模式替换功能,如果字符串不是很长,则速度会更快。这里比较麻烦,因为替换必须是字符串,所以每个字符都需要单独替换。请注意,您必须先转义反斜杠。
quoted_string=${raw_string//\\//\\\\}
for c in \[ \] \( \) \. \^ \$ \? \* \+; do
quoted_string=${quoted_string//"$c"/"\\$c"}
done
如何引用特殊字符(在 ksh93 中)
Ksh 的字符串替换结构比 bash 和 zsh 中的淡化版本更强大。它支持对模式中的组的引用。
quoted_string=${raw_string//@([][()\.^$?*+])/\\\1}
你真正想要什么
这里不需要find
:shell 模式足以匹配以三位数字结尾的文件。如果不存在零件文件,则 glob 模式不会展开。还有一种更简单的方法来添加文件大小:stat
您可以调用wc -c
(在常规文件上,在大多数系统上,wc
将查看文件大小,而不会费心打开文件并读取字节)。
set -- "$DESTINATION/$FILE_BASENAME".[0-9][0-9][0-9]
case $1 in
*\]) # The glob was left intact, so no part exists
do_split …;;
*) # The glob was expanded, so at least one part exists
FILE_SIZE_EXISTING=$(wc -c "$@" | sed -n '$s/[^0-9]//gp')
if [ "$FILE_SIZE_EXISTING" -ne "$(wc -c <"$DESTINATION/$FILE_BASENAME")" ]; then
do_split …
fi
请注意,您对总大小的测试不太可靠:如果文件已更改但大小保持不变,您最终会得到陈旧的部分。如果文件永远不会改变,那也没关系,唯一的风险是部分内容可能被截断或丢失。