以前的程式碼:
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"