我有一个项目,我试图通过 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-01
到2010-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
也就是说,foo
在bar
或baz
中搜索files
不是您想要的内容。
你要:
grep 'foo
bar
baz' files
反而。为此,你会这样做:
IFS=$'\n'
grep -- "${a[*]}" files
使用语法时,第一个字符$IFS
用于连接数组的元素"${a[*]}"
。这适用于所有支持数组的 shell(ksh
、zsh
、bash
、yash
(尽管该$'\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
我留下了一个echo
in,因此在您将其删除之前它不会执行任何操作。但上面必须将所有日期扩展为参数,所以你应该更喜欢这样:
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