我列出了目录中符合给定条件的文件。我希望对目录中的每个文件执行的操作之一是提取其 6 位数日期并将其放入变量中。我的脚本目前如下所示:
for i in $(ls $INPUT_DIR | egrep -i '^'$INPUT_FILE_PREFIX'[0-9][0-9]([0][1-9]|1[0-2])([0][1-9]|[12][0-9]|[3][01])'$INPUT_FILE_SUFFIX); do
MYDATE=$("$i" | grep -oP '\d{6,6}')
echo $MYDATE
done
以上导致错误"somefile": command not found
。
对我来说奇怪的是,如果我替换MYDATE=$("$i" | grep -oP '\d{6,6}')
为echo "$i" | grep -oP '\d{6,6}'
一切正常。
如何让我的脚本"$i"
作为字符串而不是命令传递?
答案1
看起来您正在尝试解析文件名中以 . 开头$INPUT_FILE_PREFIX
和结尾的六位数字$INPUT_FILE_SUFFIX
。
这将做到这一点:
for name in "$INPUT_DIR/$INPUT_FILE_PREFIX"??????"$INPUT_FILE_SUFFIX"; do
test -f "$name" || continue
number=${name#$INPUT_DIR/$INPUT_FILE_PREFIX}
number=${number%$INPUT_FILE_SUFFIX}
printf "Number = %s\n" "$number"
done
如果您想确保只匹配数字(匹配单个字符,无论该字符是什么),请将 every 更改?
为。[0-9]
?
循环中的参数替换会删除 值的第一部分$name
,然后删除剩余字符串的最后部分,将中间的数字(前缀和后缀之间的六个字符)保留为变量 中唯一留下的内容$number
。
命令
MYDATE=$("$i" | grep -oP '\d{6,6}')
正如您所发现的,将被解释为调用$i
命令中的任何内容。同时你说放在echo
前面"$i"
会让它起作用,它确实如此:
MYDATE=$(echo "$i" | grep -oP '\d{6,6}')
与您的代码相关:为什么*不*解析`ls`?
答案2
我会推荐一种稍微不同的循环文件名的方式——使用 bash扩展的通配符收集文件名:
shopt -s extglob
for d in "${INPUT_DIR}"/"${INPUT_FILE_PREFIX}"[0-9][0-9]@(0[1-9]|1[0-9])@(0[1-9]|[12][0-9]|3[01])"${INPUT_FILE_SUFFIX}"
do
[[ $d =~ ${INPUT_FILE_PREFIX}([[:digit:]]+)${INPUT_FILE_SUFFIX} ]]
MYDATE=${BASH_REMATCH[1]}
done
通配语法几乎与您的 grep 语句相同。每组@(...)
引入一个匹配任何给定模式的请求,这些模式由 分隔|
。我注意到 的(假定日期)模式[3]
是一个单字符类,因此我删除了它周围的括号。
一旦我们在循环中获得了文件名for
,您就可以使用 bash 的条件表达式的正则表达式=~
运算符将数字剥离到 MYDATE 中。
答案3
for i in $(ls $INPUT_DIR | egrep -i '^'$INPUT_FILE_PREFIX'[0-9][0-9]([0][1-9]|1[0-2])([0][1-9]|[12][0-9]|[3][01])'$INPUT_FILE_SUFFIX);
一般来说,没有理由ls
在这样的结构中使用,它只会使命令更难以阅读,而且您会遇到一些极端情况的问题(请参阅BashGuide 中的解析)。但是,您拥有的正则表达式不能表示为标准 shell glob,因此使用它是有一定意义的。虽然因为这被标记为巴什,我们可以在 shell 中执行此操作,或者使用extglob
[[ .. ]]
(或者使用正则表达式与更广泛的 glob 之后的构造匹配)。
shopt -s extglob
for i in "$INPUT_DIR/$INPUT_FILE_PREFIX"[0-9][0-9]@(0[1-9]|1[0-2])@(0[1-9]|[12][0-9]|3[01])"$INPUT_FILE_SUFFIX" ; do
如果你真的不需要这么严格的模式,你可以直接使用[0-9][0-9][0-9][0-9][0-9][0-9]
。
在对 的赋值中MYDATE
,我假设您只想删除前缀和后缀。 (尽管如果您的前缀/后缀包含六位数字字符串,那么 grep 也会匹配它。)
MYDATE=${i#"$INPUT_DIR/"} # remove the directory
MYDATE=${MYDATE#"$INPUT_FILE_PREFIX"} # remove the prefix
MYDATE=${MYDATE%"$INPUT_FILE_SUFFIX"} # and the suffix
在全:
shopt -s extglob
for i in "$INPUT_DIR/$INPUT_FILE_PREFIX"[0-9][0-9]@(0[1-9]|1[0-2])@(0[1-9]|[12][0-9]|3[01])"$INPUT_FILE_SUFFIX" ; do
MYDATE=${i#"$INPUT_DIR/"} # remove the directory
MYDATE=${MYDATE#"$INPUT_FILE_PREFIX"} # remove the prefix
MYDATE=${MYDATE%"$INPUT_FILE_SUFFIX"} # and the suffix
echo "$MYDATE"
done
答案4
你有没有尝试过bash 进程替换?看起来它会让你的命令方式更容易,并且不需要循环或变量。
基本上,流程替代并不为人所知,但功能却非常强大。
进程替换将一个进程(或多个进程)的输出输入到另一个进程的标准输入中。
因此,您的命令可以简化为如下所示:
grep -oP '\d{6,6}'<(egrep -i '^'$INPUT_FILE_PREFIX'[0-9][0-9]([0][1-9]|1[0-2])([0][1-9]|[12][0-9]|[3][01])'$INPUT_FILE_SUFFIX <(ls $INPUT_DIR))
通过更简单的版本可以更好地理解其背后的逻辑:
user@yrmv-191108:~/nums$ ls .
1 10 2 3 4 5 6 7 8 9
user@yrmv-191108:~/nums$ grep 1 <(ls .)
1
10