我有一个父文件夹,其中包含一系列“histogram_0000_0000”文件夹。我正在尝试制作一个 bash 脚本,在每个文件夹中搜索文件“out.txt”,并在每次在文件夹中找到该文件时返回(以检查该文件是否存在于所有文件夹中)。我得到的脚本是;
#!/bin/bash
joblist='job_list.txt'
njobs=`wc ${joblist} | awk '{print $1}'`
cwd=`pwd`
for ((i=1 ; i <= ${njobs} ; i++ )); do
folder=`awk '(NR=='${i}'){print}' ${joblist}`
echo $folder
cd ${folder}
if [ find -name "out.txt" ]
then
echo out.txt found in $folder
fi
cd ${cwd}
done
但每次运行时都会出现错误;
./checkrun.sh: 第 10 行: [: -name: 需要二元运算符
我环顾四周,尝试使用“[[”和“]]”,但仍然不知道为什么我有运气!任何帮助都会很棒。谢谢,-杰克
答案1
[
...]
测试字符串或数字表达式。如果您想查看find
命令是否匹配,您需要测试它是否提供了任何输出(非空字符串,使用 进行测试-n
)。
if [[ -n "$(find -name 'out.txt')" ]]
then
echo "out.txt found in $folder"
fi
请注意,我使用现代$(...)
语法来捕获命令的输出,而不是已弃用的反引号`...`
。
其他的建议,
您可以简化
njobs=
wc ${joblist} | awk '{print $1}'`` 用于njobs=$(wc -l <"$joblist")
获取行数。$i
作为awk
变量传入,folder=$(awk -v i="$i" 'NR==i {print}' "$joblist}")
不要
cd
进入子文件夹,然后依靠使用cd
返回到原来的位置。使用相对路径引用子目录或使用子 shell 将其保留cd
在有限的上下文中。这里我使用第一种方法echo "$folder" if [[ -n "$(find "$folder" -name 'out.txt')" ]] then echo "out.txt found in $folder" fi
如果
$folder
始终是相对路径,则在用作路径的任何地方都将其引用为“./$folder”。这可以防止它以破折号 (-
) 开头,这可能会混淆可能尝试将其解释为一系列选项的命令。shell
bash
已经有一个当前工作目录的变量,因此您可以简化cwd=`pwd`
为cwd="$PWD"
使用https://shellcheck.net/检查您的代码。 (如果您担心共享私有代码,则可以在本地安装它。)
答案2
#!/bin/bash
joblist='job_list.txt'
njobs=`wc ${joblist} | awk '{print $1}'`
cwd=`pwd`
while IFS= read -r line
do
folder="$line" #`awk '(NR=='${i}'){print}' ${joblist}`
echo $folder
cd ${folder}
if [ -f out.txt ]
then
echo out.txt found in $folder
fi
cd ${cwd}
done < job_list.txt
我希望这有帮助。更容易一点......line=current-line-from'job_list.txt'
它逐行读取,所以文件看起来像这样:
folder1
folder2
folder234
我的输出:
france1@macubuntu:/tmp/tmdf$ bash script.sh
folder1
folder2
folder234
out.txt found in folder234
france1@macubuntu:/tmp/tmdf$
希望我有帮助...
答案3
我会用zsh
它来代替bash
:
#! /bin/zsh -
joblist='job_list.txt'
dirs=( ${(f)"$(<$joblist)"} )
for dir in $dirs; do
first_out=( $dir/**/out.txt(NDY1) )
if (( $#first_out )); then
print -r out.txt found in $dir
fi
done
对于bash
其他 GNU find
,等价物是:
#! /bin/bash -
joblist='job_list.txt'
dirs=(); readarray -t dirs < "$joblist"
for dir in "${dirs[@]}"; do
[[ -n $dir ]] || continue
[[ $dir = [/.]* ]] || dir=./$dir
first_out=$(find "$dir" -name out.txt -print -quit)
if [[ -n $first_out ]]; then
printf '%s\n' "out.txt found in $dir"
fi
done
一些注意事项:
readarray -t lines < file
可以将文件的行存储到数组中,但请注意,如果file
无法打开,readarray
甚至不会运行,因此$dirs
不会被设置,因此预先初始化变量很重要。bash
最终添加了对 zsh 风格的递归通配符 (**/
) 选项的支持globstar
。然而,在符号链接处理方面它仍然有些问题(行为随版本而变化),而且更重要的是在这种情况下,它仍然没有添加对 glob 限定符的支持,例如 forN
、nullglob
forD
或dotglob
停止Y1
在第一个匹配甚至不用对上面使用的列表进行排序。find
find "$dir"
如果$dir
以 开始,则会闯入-
。虽然find
确实支持--
标记选项的结尾,但这在这里没有帮助,因为-
如果不是选项,则以 开头的参数仍将被视为谓词(如(
,)
or!
)。这就是为什么我们在前面加上./
不是$dir
绝对的并且尚未以 开头的 s.
(因此-my-annoying-dir-
变为./-my-annoying-dir-
)。 BSDfind
支持find -f "$dir"
避免该问题,但 GNU 尚不支持find
。- 如果您只想检查至少一个是否存在
out.txt
,则没有必要找到所有这些。您可以在第一场比赛时停止。因此(GNU 特定的)-quit
(一些 BSD-exit
对此有规定)。 - zsh
${(f)"$(<file)"}
在换行符上进行分割,但是当不加引号时,会删除空元素(如果没有,$dirs
下面的下一个不加引号的元素将删除它们)。空路径通常不是有效路径,因此应该会产生错误,但是当与 串联时/something
,它们会令人烦恼地变成不相关的可能有效的绝对路径。当前面加上./
成为当前目录时。所以最好在这里跳过它们,因此[[ -n $dir ]] || continue
在 bash 变体中。