问题:
我有一个文件名列表,filenames.txt:例如.
/usr/share/important-library.c
/usr/share/youneedthis-header.h
/lib/delete/this-at-your-peril.c
Run Code Online (Sandbox Code Playgroud)
我需要重命名或删除这些文件,我需要在项目目录树中找到对这些文件的引用:/home/noob/my-project/所以我可以删除或更正它们.
我的想法是使用bash来提取文件名:basename filename然后使用for循环在项目目录中grep它.
FILELISTING=listing.txt
PROJECTDIR=/home/noob/my-project/
for f in $(cat "$FILELISTING"); do
extension=$(basename ${f##*.})
filename=$(basename ${f%.*})
pattern="$filename"\\."$extension"
grep -r "$pattern" "$PROJECTDIR"
done
Run Code Online (Sandbox Code Playgroud)
我可以把这个项目弄得一团糟 - 有没有人看到我逻辑上的缺陷; 更好:你是否看到一个更可靠的可扩展方式来在一个巨大的目录树上执行此操作?让我们假设版本控制不在桌面上(事实上).
一些评论:
代替
for f in $(cat "$FILELISTING") ; do
...
done
Run Code Online (Sandbox Code Playgroud)
写起来有点安全
while IFS= read -r f ; do
...
done < "$FILELISTING"
Run Code Online (Sandbox Code Playgroud)
这样,您的代码对文件名中的空格,制表符,星号等没有任何问题(尽管它仍然不支持换行符).
你的目标是f分成extension和filename,然后重新组装它们\.,似乎是你希望文件名被视为文字字符串; 对?比如,你担心grep将."任何角色"视为"任何角色",而不是"一个点".更通用的解决方案是使用grep's -F选项,它告诉它将模式视为固定字符串而不是正则表达式:
grep -r -F "$f" "$PROJECTDIR"
Run Code Online (Sandbox Code Playgroud)你的介绍提到使用basename,但你实际上并没有使用它.这是故意的吗?
如果您不使用basename是故意的,那么filenames.txt实际上只包含要搜索的模式列表; 你甚至不需要写一个循环,在这种情况下,因为grep's -f选项告诉它从文件中取一个换行符分隔的模式列表:
grep -r -F -f "$FILELISTING" "$PROJECTDIR"
Run Code Online (Sandbox Code Playgroud)
您应该使用类似的东西备份您的项目tar -czf backup.tar.gz "$PROJECTDIR"."修改控制已脱离桌面"并不意味着你无法拥有回滚策略!
编辑添加:
要立即传递所有基本名称grep,希望它可以更聪明地使用它们,而不仅仅是循环遍历它们,就好像调用是分开的一样,你可以编写如下内容:
grep -r -F "$(sed 's#.*/##g' "$FILELISTING")" "$PROJECTDIR"
Run Code Online (Sandbox Code Playgroud)
(为了简洁起见,我使用的sed不是while+ basename,但"$(...)"如果您愿意,可以在整个循环中使用.)