从文件中 grep 数组并重用搜索模式

从文件中 grep 数组并重用搜索模式

我有一个项目,我试图通过 shell 脚本来完成。

我有大约 30 年的长期每周广播节目的目录。由于它们来自不同的来源,因此名称的格式可能截然不同。这使得我很难知道我有哪些节目以及我缺少哪些节目。

我想以标准日期格式创建符号链接,并将文件名作为日期符号链接到实际的显示目录(如果有)。

例如,我想做

'2015-09-25' -> '../Radio Show/2015-09-25 Special Guest/'
'2015-10-02' -> '../Radio Show/Very funny! 2015-10-02 Show'

日期格式也多种多样,但现在我只是担心找到 YY-MM-DD 和 YYYY-MM-DD 格式。

所以我创建了一个文件,其中每一行都是一个日期,从1980-01-012010-12-31,使用这个答案

然后,我读取每一行,用于find查找名称中包含该字符串的目录。然而,find对整个目录树上 30 年前的每个日期执行一次操作需要很长时间*。

所以,我曾经find -type d . > filesystem.txt创建一个包含所有目录名称的文件。然后我可以只grep针对该文件中的每个日期字符串,而不是find在磁盘上为每个日期字符串运行 a 。

但是,我在将日期文件中的每一行加载到 grep 中时遇到问题。

我可以使用$ grep -f dates.txt filesystem.txt But 这给我所有结果,格式如下:

./complete/1996-02-18
./complete/1996-03-03
./complete/1996-03-31
...

我不知道如何使用字符串参数得到结果来得到这个:

'1996-03-31' -> './complete/1996-03-31'

我已经尝试过了$ grep "${dates[@]}" metadata/filesystem.txt,但这并没有达到我的预期:

grep: 1988-01-03: No such file or directory
grep: 1988-01-04: No such file or directory

这是我想要做的伪代码版本:

foreach ( date-string in dates.txt ) {
  grep date-string in filesystem.txt
  if (match) {
     ln -s match date-string
  }
}

我怎样才能在 bash 中做到这一点?

-* 我可以通过不使用每个日期来简化这一点,但我不确定广播节目是否在其所有历史记录中都发生在同一天。我想确保不会错过任何一个日期,因此我想使用 30 年跨度内的所有日期。

答案1

回答主题中的问题:如何使用 grep 查找数组中的任意元素

a=(foo bar baz)
grep "${a[@]}" files

将会:

grep foo bar baz files

也就是说,foobarbaz中搜索files不是您想要的内容。

你要:

grep 'foo
bar
baz' files

反而。为此,你会这样做:

IFS=$'\n'
grep -- "${a[*]}" files

使用语法时,第一个字符$IFS用于连接数组的元素"${a[*]}"。这适用于所有支持数组的 shell(kshzshbashyash(尽管该$'\n'部分还不能工作yash,但您需要在那里使用文字换行符))。

使用zsh,您还可以执行以下操作:

grep -e$^a files

其扩展为

grep -efoo -ebar -ebaz files

这是搜索不同字符串的另一种方法。

(请注意,如果数组包含要搜索的固定字符串而不是要匹配的正则表达式,则应使用该-F选项)。

答案2

zsh

autoload zmv # best in ~/.zshrc
zmv -Ls -n '../Radio Show/(^*[0-9])((19|)(<80-99>~^??)|(20|)(<0-16>~^??))(-<1-12>-<1-31>~^-??-??)(^[0-9]*)' '${4:+19$4}${6:+20$6}$7'

-n用于空运行。当对建议的操作感到满意时,删除以实际进行链接。

zmv负责避免冲突或覆盖文件。具体zsh的全局运算符如下:

  • <1-12>匹配解析为 1 到 12 之间的十进制整数的字符串。请注意,它匹配 2012 年的 012。
  • ^x: 否定
  • x~y(and-not):只要不匹配 y 就匹配 x 的字符串。因此<1-12>~^??匹配从 1 到 12 的 2 位数字(匹配 01,但不匹配 1 或 0001)。
  • (x|y):像 ERE 中那样交替。

它确实以 YY-MM-DD 格式插入缺失的 19 或 20 日期。

答案3

John1024 的答案可能是最好的,但为了完整起见,这里是您的伪代码实现:

for datestring in $(cat dates.txt)
do if match="$(grep "$datestring" filesystem.txt)"
   then echo ln -s "$match" "$datestring"
   fi
done

我留下了一个echoin,因此在您将其删除之前它不会执行任何操作。但上面必须将所有日期扩展为参数,所以你应该更喜欢这样:

while read datestring
do if match="$(grep "$datestring" filesystem.txt)"
   then echo ln -s "$match" "$datestring"
   fi
done <dates.txt

$datestring尽管我们知道它没有空格,但我还是加上了双引号,所以这不会改变任何东西。

答案4

如果我理解正确的话,您有一个 filesystem.txt 文件,如下所示:

$ cat filesystem.txt 
../Radio Show/Very funny! 2015-10-02 Show
../Radio Show/2015-09-25 Special Guest/

考虑一下:

$ sed -E 's/.*[^[:digit:]]([[:digit:]]{2,4}-[[:digit:]]{2}-[[:digit:]]{2}).*/ln -s "&" "\1"/' filesystem.txt >script

上面创建了一个名为script. script看起来像一系列bash命令:

$ cat script
ln -s "../Radio Show/Very funny! 2015-10-02 Show" "2015-10-02"
ln -s "../Radio Show/2015-09-25 Special Guest/" "2015-09-25"

检查此文件,如果它看起来符合您的要求,则执行它:

bash script

相关内容