![为什么这个脚本在使用 crontab 自动运行时会输出损坏的文件?](https://linux22.com/image/150907/%E4%B8%BA%E4%BB%80%E4%B9%88%E8%BF%99%E4%B8%AA%E8%84%9A%E6%9C%AC%E5%9C%A8%E4%BD%BF%E7%94%A8%20crontab%20%E8%87%AA%E5%8A%A8%E8%BF%90%E8%A1%8C%E6%97%B6%E4%BC%9A%E8%BE%93%E5%87%BA%E6%8D%9F%E5%9D%8F%E7%9A%84%E6%96%87%E4%BB%B6%EF%BC%9F.png)
我有一个脚本可以扫描文件夹中的所有 .mp3 文件并创建索引。接下来等待5秒,否则无法正常工作。最后,它从歌曲名称中删除了不好的字符。
我使用 sudo crontab 自动运行它:
#!/bin/bash
#Creates index file.
find /var/www/html/uploads/Music/ -name '*.mp3' > /var/www/html/uploads/Music/songs.index | sleep 5s | sudo sed -i -e 's/\/var\/www\/html/\.\./g' /var/www/html/uploads/Music/songs.index
由于某种原因,当它通过 crontab 运行时,它会创建一个文件,该文件必须转换为文本并返回索引才能读取它。当您手动运行相同的脚本时,它工作正常。我缺少什么?
crontab 文件:
1 * * * * /home/aaeadmin/bin/midnightRun.bash
答案1
这是可能因为您在管道中运行命令。
管道的每个部分同时启动并与同一管道的其他部分同时运行。这意味着该find
命令与该命令完全同时启动sed
。只有通过管道传递的数据(从一个命令的标准输出到下一个命令的标准输入)才能同步不同的命令。
另请注意,您实际上并没有使用管道的管道方面。命令之间没有传递数据。
你的脚本最好写成
#!/bin/bash
#Creates index file.
find /var/www/html/uploads/Music/ -name '*.mp3' > /var/www/html/uploads/Music/songs.index
sed -i -e 's/\/var\/www\/html/\.\./g' /var/www/html/uploads/Music/songs.index
在这里,find
命令将在命令开始之前完成执行sed
。我还删除了该sudo
命令,因为它显然不需要(如果find
可以写入文件,则sed
可以在没有 的情况下读取和修改它sudo
)。
如果你发现你做如果需要sudo
写入songs.index
,我建议您在属于具有写入目标目录权限的用户的 crontab 中运行此 cron 作业/var/www/html/uploads/Music
。
管道式解决方案是
#!/bin/sh
#Creates index file.
find /var/www/html/uploads/Music/ -name '*.mp3' |
sed -e 's,^/var/www/html,..,' >/var/www/html/uploads/Music/songs.index
在这里,find
写道直接地命令sed
,并将命令的结果sed
写入索引文件。来自find
into的数据(路径名)通信sed
使两个进程保持同步,并且sed
将等待用于find
在继续之前生成下一行输入(反之亦然;在尝试输出更多数据之前find
会等待sed
处理数据)。
我还使sed
命令更易于阅读,并将正则表达式锚定到行的开头,并删除了g
末尾不必要的标志。由于它现在从find
命令中读取,我还删除了该-i
选项(严格来说,该-e
选项也可以删除)。
我改变的另一件事是线路#!
。您没有使用任何bash
特定功能,因此我们也可以使用(可能)更轻量级的 shell 来运行脚本。
如果您想将找到的文件的文件名写入索引,并将首字母/var/www/html
替换为 ,..
您也可以直接从以下位置执行此操作find
:
#!/bin/sh
#Creates index file.
find /var/www/html/uploads/Music/ -type f -name '*.mp3' -exec sh -c '
for pathname do
printf "../%s\n" "${pathname#/var/www/html/}"
done' sh {} + >/var/www/html/uploads/Music/songs.index
这个find
命令将找到名称与给定模式匹配的所有常规文件(即不是目录等)的路径名。对于批量的这些路径名,将调用一个简短的内联 shell 脚本。该脚本只是迭代当前批次的路径名并稍微修改后将其打印出来。
对路径名的修改是通过参数替换来完成的${pathname#/var/www/html/}
。这删除/var/www/html/
从 中的值开始的字符串$pathname
。然后使用的格式printf
字符串将确保这部分被替换为../
.
也可以看看