我有一个目录列表(或者文件夹列表,如果你愿意的话)。
每个目录中都有一个名为 的文件<something>.txt
,该文件是从数据库转储生成的。
我想运行一个 cron 作业,遍历并将其重命名
<something>.txt
为 to "${PWD##*/}"_info.txt
,但前提是该*.txt
文件存在,因为当该文件丢失时,命令mv
会失败,然后尊重它cd ..
不断向上移动目录,无法完成剩余的检查哪些是故意的不是在下面的代码片段中进行了说明或指示。
所以代码 CONTEXT 是:
对于 f 在<目录列表> 做 cd“${f}” 如果[-e“*.txt”];然后 ← 这是问题的部分 mv *.txt "${PWD##*/}".txt ← 剩下的只是提供上下文 菲 光盘 .. 完毕
对这个小片段有专家的看法吗?
我清理了这个例子。关键部分是条件:
[ -e "*.txt" ]
即使<something>.txt
文件存在,条件也会失败。
这就是我要问的。
答案1
一种可能性是使用 glob 中的这些项目创建一个数组,然后您可以查看该数组是否有任何元素。例如:
cur_opt=$(shopt -p nullglob) # get current status so we can restore
shopt -s nullglob # to let the glob expand to nothing
txt_files=( *txt )
$cur_opt # restore nullglob setting to what it was before
if [ ${#txt_files} -gt 0 ]; then
...
fi
那么如果你想对文件进行操作,你也可以迭代数组,而不仅仅是测试数组是否有元素
如果你真的只是想看看是否存在任何匹配的东西,你可以使用compgen
像
if [ ! -z "$(compgen -G "*.txt") ]; then
...
fi
这也可以只是检查回报,compgen
所以可能是
if compgen -G "*.txt" &>/dev/null; then
...
fi
答案2
专家对这个小片段的看法:
- 如果您正在处理目录列表和一堆文件(名称事先未知),请不要使用
f
来表示目录名称。这实在是太令人困惑了。 当您单独处理 shell 变量时(不与其他任何内容连接),简单地,不进行修改(例如,通过
##
、%
或),您不需要花括号:
。/
看我什么时候应该在变量周围添加双引号以"${var}"
防止出现问题?所以,我建议外循环应该是这样的:
对于 d 在<目录列表> 做 cd“$d” (在每个目录中执行的代码) 光盘 .. 完毕
除了 @Eric Renouf 的完美答案之外,还有几种方法可以编写(在每个目录中执行的代码)。这是一个简单的,接近您尝试过的:
for f in *.txt
do
if [ -e "$f" ]
then
mv -- "$f" "${PWD##*/}".txt
fi
done
<something>.txt
如果目录中只有一个文件,那么这将执行您想要和期望的操作:for f …
循环将运行一次,$f
其名称等于文件名,并且文件将被重命名。 (我用来mv --
防止名称以-
否则将被解释为选项。
mv ./"$f" …
也可能是安全的,只要您确定这$f
是当前目录中的文件,或者是相对路径名。)如果目录中没有*.txt
文件,那么循环也会运行一次,具有$f
相同的效果(字面意思)到*.txt
。由于没有该名称的文件,因此-e
测试将失败并且mv
不会执行。
如果目录中有多个文本文件,则会出现问题。如果有n这样的文件,然后循环将运行n次,每个文本文件将被重命名为"${PWD##*/}".txt
.作为扎卡里·布雷迪指出,这可能会导致最后一个文件覆盖第一个文件n−1 个文件。解决此问题的一种方法是将-i
选项添加到mv
命令中,这样您就会在任何覆盖现有文件的尝试时收到警告,并给出批准或拒绝的选项。当然,如果脚本作为cron
作业运行,则这没有用。另一种方法是将break
命令添加到循环中:
对于 *.txt 中的 f 做 如果[-e“$f”] 然后 mv -- "$f" "${PWD##*/}".txt 休息 菲 完毕
这将导致循环只执行一次,因为当它运行时,它会执行break
终止循环的命令。这样,只有第一个文本文件将被重命名,其他任何文件都将被保留。您不会收到有多个文件的通知。
一种稍微复杂的方法是检测和处理多文件情况:
第一个=1 对于 *.txt 中的 f 做 如果[-e“$f”] 然后 如果[“$第一个”] 然后 mv -- "$f" "${PWD##*/}".txt 首先= 别的 printf "警告:文件 %s 未重命名。\n" "$f" 菲 菲 完毕
其中first
标志在循环开始时设置,然后在第一次迭代期间清除,因此后续迭代知道它们不是第一次。或者,您可以检查是否"${PWD##*/}".txt
已经存在。