如何将一个变量上的 grep 命令的结果获取到另一个变量中

如何将一个变量上的 grep 命令的结果获取到另一个变量中

我列出了目录中符合给定条件的文件。我希望对目录中的每个文件执行的操作之一是提取其 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

相关内容