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
    文件
我知道我可以用Python来做这件事。但这看起来像作弊。我还知道我可以查找较新的
*.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
您可能需要设置Bash
nullglob
选项:

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
文件早(或与之相同),则条件为真