以前的代码:
total=`ls -Rp rootfs | wc -l`
count=0
当我为变量分配一个简单的加法时:
sudo find rootfs -exec cp -d -- "{}" "/media/$USER/{}" 2> /dev/null \; -exec sync \; -exec count=$((count+1)) \; -exec echo -en "\rcopiati: $count/$total" \;
我得到:
find: ‘count=1’: No such file or directory
当我执行时:
sudo find rootfs -exec cp -d -- "{}" "/media/$USER/{}" 2> /dev/null \; -exec sync \; -exec count=1 \; -exec echo -en "\rcopiati: $count/$total" \;
我犯了同样的错误。为什么?
对于复制的每个文件,我想要计数器:1/13444,更新为 2/13444、3/13444 等...
编辑:
我找到了一种方法,但它看不到隐藏文件,我怎样才能让他们在 for 循环中看到它们?
#!/bin/bash
copysync() {
countfiles() {
for f in $1/*; do
if [ -d "$f" ]; then
countfiles "$f"
else
if [ "${f: -2}" != "/*" ]; then
total=$((total+1))
fi
fi
done
}
recursivecp() {
for f in $1/*; do
if [ -d "$f" ]; then
mkdir -p "/media/$USER/$f"
recursivecp "$f"
else
if [ "${f: -2}" != "/*" ]; then
sudo cp -a "$f" "/media/$USER/$f"
sudo sync
count=$((count+1))
echo -en "\rCopied: $((count*100/total))%"
fi
fi
done
}
total=0
countfiles $1
count=0
recursivecp $1
}
copysync rootfs
答案1
shellcount=$((count+1))
在运行之前会展开find
。
然后find
将尝试将参数-exec
作为命令执行。这必须是程序或脚本,不能是 shell 内置函数或用于变量赋值的 shell 语法。
无法以这种方式计算找到的文件数,因为find
会为 启动一个新进程-exec
,因此变量赋值的结果在父 shell 中不可用。
我建议为找到的每个文件打印一行并通过管道输出find
to wc -l
,例如
find rootfs -exec cp -d -- "{}" "/media/$USER/{}" \; -exec sync \; -print|wc -l
要在复制文件时获得一些输出,您可以使用如下命令:
find rootfs|while IFS= read -r file
do
cp -d -- "$file" "/media/$USER/$file"
sync
count=$((count+1))
echo -en "\rcopiati: $count/$total"
done
评论:
这不适用于包含换行符(可能还有其他特殊字符)的文件名。
rootfs
如果包含子目录,该脚本可能无法工作。您应该处理这种情况或使用find
的选项-maxdepth
来-type f
避免此问题。
答案2
看起来好像您正在尝试使用 执行每个命令-exec
。这在一般情况下不起作用,因为-exec
仅执行外部命令。
相反,调用单个内联脚本并让其find
充当该脚本中循环的生成器:
find rootfs -type f -exec sh -c '
for pathname do
cp -d "$pathname" "/media/$USER" &&
echo . &&
sync
done' sh {} + | wc -l
这将找到目录中或目录下的所有常规文件rootfs
。对于批量的这些文件,sh -c
将调用一个简短的内联脚本。此脚本将每个文件复制到给定目录,为每个成功复制的文件输出一个点,后跟一个换行符,并调用sync
.
计算wc -l
输出的点数并报告该计数。我们不计算路径名本身,因为如果任何路径名包含嵌入的换行符,则此计数会产生误导。
如果不使用find
,这可以在例如中完成,bash
如下所示:
shopt -s globstar dotglob nullglob
for pathname in rootfs/**/*; do
[[ ! -f $pathname ]] && continue
cp -d "$pathname" "/media/$USER" &&
echo . &&
sync
done | wc -l
如果设置了 shell 选项,则使用包含 glob 的 globbing 模式**
,该模式会匹配 inte 子目录。globstar
我还设置dotglob
为能够看到隐藏的名称,以及nullglob
shell 选项,以便在模式与任何内容都不匹配时完全避免运行循环。
同样的事情,但有一个计数器:
shopt -s globstar dotglob nullglob
count=0
for pathname in rootfs/**/*; do
[[ ! -f $pathname ]] && continue
cp -d "$pathname" "/media/$USER" &&
count=$(( count + 1 ))
sync
done
printf 'count=%d\n' "$count"