Shell 查找比名称相似但扩展名不同的文件更新的文件
这是一个相当简单的问题,一直困扰着我。一点背景故事。我有一个装满脚本的文件夹。这些脚本获取数据文件Shell 查找比名称相似但扩展名不同的文件更新的文件,shell,find,file-comparison,Shell,Find,File Comparison,这是一个相当简单的问题,一直困扰着我。一点背景故事。我有一个装满脚本的文件夹。这些脚本获取数据文件*.dat,并在*.eps中生成输出。我的脚本的扩展名是*.plt。我创建一行shell脚本,运行该文件夹中的所有*.plt文件 #!/bin/sh find . -name "*.plt" -exec {} \; 我只想确保我将在文档中使用的所有*.pdf图像都是最新的。有一段时间,一行脚本很好。但当文件数超过50时,运行需要一些时间。我很少更改数据文件,但经常更改*.plt脚本。脚本的编写方式
*.dat
,并在*.eps
中生成输出。我的脚本的扩展名是*.plt
。我创建一行shell脚本,运行该文件夹中的所有*.plt
文件
#!/bin/sh
find . -name "*.plt" -exec {} \;
我只想确保我将在文档中使用的所有*.pdf
图像都是最新的。有一段时间,一行脚本很好。但当文件数超过50时,运行需要一些时间。我很少更改数据文件,但经常更改*.plt
脚本。脚本的编写方式是,名为this\u script\u does.plt
的脚本将创建一个名为this\u script\u does.eps
的文件
因此,我的问题是
- 有没有办法编写一个精练的shell脚本,只执行比类似的
更新的*.eps
文件*.plt
*.eps
,并执行比此更新的所有*.plt
。对于大多数实际情况,这将解决我的问题。当我输入问题时,我才意识到这个选项,所以谢谢SX。然而,作为一个教学练习,为了解决我最初的疑问,我想搜索个别案例:将每个*.plt
的修改时间与每个*.eps
的修改时间进行比较,并且仅当它们比输出时间晚时才执行脚本。可能吗?能在一条线上完成吗
编辑:我忘了添加,*.plt
脚本也应该在没有同名*.eps
文件时执行,这通常意味着脚本是新的,尚未执行。我想我应该使用:
#!/bin/bash
for plt in *.plt
do
eps=$(basename "$plt" .plt).eps
if [ "$plt" -nt "$eps" ]
then "$plt"
fi
done
这使用Bash/kornshell操作符-nt
表示'newer-than'(相反的-ot
操作符表示'older-than')。我假设所有文件都在一个目录中,因此不需要递归搜索。如果不正确,则使用单独的:
find . -type d -exec sh -c "cd {}; new-script.sh" \;
(其中newscript.sh
是我刚才显示的脚本)。或者使用Bash扩展名**
操作符:
for plt in *.plt **/*.plt
您可能需要设置Bashnullglob
选项:
shopt -s nullglob
当扩展与任何文件不匹配时,这不会生成任何内容
当
.eps
文件不存在时也会生成:
#!/bin/bash
for plt in *.plt
do
eps=$(basename "$plt" .plt).eps
if [ ! -f "$eps" ] || [ "$plt" -nt "$eps" ]
then "$plt"
fi
done
这个函数中唯一不完全通用的shell特性是
-nt
操作符。如果您的/bin/sh
不支持它,请检查/bin/[
命令-它可能-或者使用Korn Shell或Bash,而不是shebang行中的/bin/sh
。此脚本应该完成您所期望的:
find . -name "*.eps" -exec sh -c \
'plt=$(basename "$1" eps)plt; [ "$plt" -nt "$1" ] && $plt' sh {} \;
它将递归到子目录中(如果有)。如果您不想这样做,并且使用GNU find,一个简单的解决方法是运行:
find . -maxdepth 1 -name "*.eps" -exec sh -c \
'plt=$(basename "$1" eps)plt; [ "$plt" -nt "$1" ] && $plt' sh {} \;
如果不使用GNU find,则可以使用该语法:
find *.eps -type f -exec sh -c \
'plt=$(basename "$1" eps)plt; [ "$plt" -nt "$1" ] && $plt' sh {} \;
但是,如果有大量文件与*.eps
模式匹配,则后者可能会失败,并出现“arg list too long”错误。任何基于for file in*.extension
循环的解决方案都会遇到同样的问题
还要注意的是,-nt
不是由POSIX指定的,因此根据您的系统,您可能需要特别说明要使用的shell,而不是sh
(主流shell,如dash
,bash
,ksh
,ksh93
或zsh
都支持-nt
)。例如,在Solaris 10上,您将使用:
find . -name "*.eps" -exec ksh -c \
'plt=$(basename "$1" eps)plt; [ "$plt" -nt "$1" ] && $plt' ksh {} \;
编辑:
由于脚本应在.eps
文件不存在时运行,因此命令应在.plt
文件上循环,例如:
find *.plt -type f -exec bash -c \
'eps=$(basename "$0" plt)eps;
[ ! -f "$eps" -o "$0" -nt "$eps" ] && "$0"' "{}" \;
酷。很接近。我忘了在我的问题中提到当
*.eps
文件不存在(新创建的脚本)时脚本也应该运行。出于某种原因,我对最后一部分有问题:sh{}
。我的脚本是gnuplot
,第一行(解释器)是#!/usr/bin/gnuplot
。我还应该添加sh吗?我已经删除了sh
,虽然它不应该真的很痛。可能是{}
我引用了gnuplot,以防万一。它仍然有问题。也许我是想请求一个单行命令。可能是我的bashrc上的某个东西让它死掉了。我的别名列表是从别人那里继承的。我真的需要清理一下。谢谢。我会接受另一个NSWER我的上一个脚本中有一个错误,我使用了一个不再存在的变量。现在应该修复。谢谢@jlliagre。我已经根据另一个答案的结构修改了我的脚本。我将很快测试你的。再次感谢第一个脚本完全符合我的要求(是的,所有*.plt
都在同一个文件夹中)。我只会在if测试中添加一个额外的条件,以便在$eps
不存在时执行。我尝试了复杂的单行程序,但这种方法很简单。我建议在尝试比较现有文件和不存在文件的时间之前,先测试不存在的文件。否则,这可以正常工作。-s
检查f或者一个非空(但现有)文件;-f
不介意该文件是否为空。类似于if[!-f“$eps”]| |[“$plt”-nt“$eps”]
我删除了我以前的评论,因为我无法编辑它。谢谢我的意思是:如果[!-s“$eps”]|[“$plt”-nt“$eps”]
。命令从左到右求值并短路,因此如果$eps
文件不存在,则条件为真;如果确实存在,但比$plt
文件早(或与之相同),则条件为真