![避免 awk 脚本中 ls 的空格和撇号问题](https://linux22.com/image/185780/%E9%81%BF%E5%85%8D%20awk%20%E8%84%9A%E6%9C%AC%E4%B8%AD%20ls%20%E7%9A%84%E7%A9%BA%E6%A0%BC%E5%92%8C%E6%92%87%E5%8F%B7%E9%97%AE%E9%A2%98.png)
我在两个(本地)设备中有许多相同的文件,碰巧我只在一个设备(A)中重命名了一些文件。我找到了以下方法来根据设备 A 中的新名称重命名另一个设备 (B) 中的相同文件,这样最后我就有了相同的文件和名称文件,不删除或复制任何内容, 但只有重命名。
事实上我发现一个有趣的答案是这里来自 Hai Vu(第二个答案)。
剧本作品,但是有两个问题:
- 和空间在文件名中:名称被空格截断。和
- 和波斯文件名中的 (')
脚本(rename-identical-files.awk)是
/^total/ {next} # Skip the first line (which contains the total, of ls -l)
{
if (name[$5] == "") {
name[$5] = $NF
print "# File of size", $5, "should be named", $NF
} else {
printf "mv '%s' '%s'\n", $NF, name[$5]
}
}
它是从命令行调用的(在目标文件夹中):
awk -f ~/rename-identical-files.awk <(ls -l /model-folder-path) <(ls -l) | sh
问题似乎是LS命令,它(至少)对文件名中的空格和其他字符有限制。
我应该编写什么代码来避免空格(和撇号)问题?
答案1
在这里,您可以使用zsh
:
#! /bin/zsh -
zmodload zsh/stat || exit
typeset -A size_to_name
model_folder=${1?}
for f in $model_folder/*(ND.); do
stat -LA size +size -- $f &&
size_to_name[$size]=$f:t &&
print -r "# File of size $size should be named ${(q)f:t}"
done
for f in *(ND.); do
stat -LA size +size -- $f &&
(($+size_to_name[$size])) &&
[[ $f != $size_to_name[$size] ]] &&
print -r mv -i -- ${(qq)f} ${(qq)size_to_name[$size]}
done
(运行为that-script /model-folder-path
)
无论文件名包含哪些字符或非字符,哪个都应该正常工作。
sh
一旦你确认它是正确的,就通过管道传输。请注意,我们不检查两个文件大小相同的情况。在这种情况下,将选择词汇顺序中的最后一个(如果a
model_folderz
中的大小均为 42,则任何大小为 42 的文件都将被重命名为z
当前文件夹中的文件(不过,-i
您将有机会避免用以下命令覆盖第一个文件)第二))。
答案2
使用校验和来识别两个目录之间的相同文件会更准确:使用 bash 4.3+ 你可以这样做
getFiles() {
local -n _files=$1
local dir=${2:-.}
cd "$dir"
for file in *; do
[[ -d $file ]] && continue
read sum name < <(md5sum "$file")
_files[$sum]="$file"
done
cd -
}
declare -A pwdFiles
getFiles pwdFiles
declare -A modelFiles
getFiles modelFiles /model-folder-path
for sum in "${!pwdFiles[@]}"; do
if [[ -v modelFiles[$sum] ]]; then
mv -v "${pwdFiles[$sum]}" "${modelFiles[$sum]}"
fi
done
答案3
假设这将是修复先前重命名错误的一次性操作,那么简单的差异有什么问题,例如(未经测试):
for newfile in *; do
for oldpath in /model-folder-path/*; do
if ! diff -q "$oldpath" "$newfile" >/dev/null; then
oldfile=${oldpath##*/}
if [[ "$oldfile" != "$newfile" ]]; then
if [[ -f "$oldfile" ]]; then
echo "clash on $oldfile" >&2
exit 1
fi
mv -- "$newfile" "$oldfile"
fi
fi
done
done