我已经在网络上找到了我想要做的每件事的零碎内容,但没有一个完全适合我的用例。
我正在尝试编写一个脚本,将多个特定文件以及具有特定文件扩展名的文件复制到桌面上的目录。它还必须在 bash 3.2 上运行。
这条线是让我绊倒的那条线。我意识到它可能与模式匹配、顺序、扩展和通配符有关。
#!/usr/bin/env bash
declare -a backup_files=(*.{id,nsf}, "desktop8.ndk", "archive", "user.dic");
declare -a notes_data_directory=~/Library/Application\ Support/IBM\ Notes\ Data/;
declare -a notes_backup_directory=~/Desktop/Notes\ Backup;
mkdir "$notes_backup_directory";
cd "$notes_data_directory";
for i in "${backup_files[@]}"
do
cp -nipvR "$notes_data_directory$i" "$notes_backup_directory";
# Also tried this
# find "$notes_data_directory""$i" -exec cp -nipvR {} "$notes_backup_directory" \;
done
cd -
当我echo "$backup_files"
这就是我得到的:
*.{id,nsf}, desktop8.ndk, user.dic
我确信这与语法有关。我只是不知道在哪里。
答案1
在某些文本周围加上引号会禁用扩展,例如通配符扩展和大括号扩展。记住这一点的逻辑方法是它们生成单词列表,但引号等上下文确保内容是单个单词。
此外,你还有虚假的逗号。在 shell 脚本中,单词之间用空格分隔。
declare -a backup_files=(*.{id,nsf} "desktop8.ndk" "user.dic")
你可以把它写成
backup_files=(*.{id,nsf} "desktop8.ndk" "user.dic")
除非您还想backup_files
在函数中声明为局部变量。
请注意,如果其中一个模式与任何文件都不匹配,它将保持不展开状态。例如,在空目录中,您最终将得到四个元素*.id
、*.nsf
和desktop8.ndk
。user.dic
如果您只想将这些传递给rm -f
,则没关系。更一般地,您可以通过在处理时检查每个元素是否是现有文件来解决这个问题,例如
for x in "${backup_files[@]}"; do
if [[ ! -e "$x" ]]; then continue; fi
…
done
(顺便说一句,请注意,访问数组的元素是带引号的字符串产生单词而不是单词列表的规则的一个例外。通过数组扩展,双引号可确保每个元素最终作为单个单词,而不是进行模式匹配和单词分割。这"$@"
是唯一的例外,包括诸如 之类的变体。)
或者,您可以设置nullglob
选项,使模式匹配在没有匹配任何文件时生成空列表。
shopt -s nullglob
backup_files=(*.{id,nsf} "desktop8.ndk" "archive" "user.dic")
那么数组将只包含现有的.id
和.nsf
文件,但无论如何它都会包含desktop8.ndk
和。user.dic
如果这些文件不存在,您也可以通过将它们设为模式来排除它们。
shopt -s nullglob
backup_files=(*.{id,nsf} desktop8.nd[k] archiv[e] user.di[c])
请注意,定义数组时,大括号和通配符会展开。因此模式匹配当前目录中的文件。您的脚本做了一些奇怪的事情,可能是无意的:您正在收集当前目录中的文件名称,并从不同的目录复制同名的文件$notes_data_directory
。如果你想匹配不同目录中的文件,你需要这么说。
notes_data_directory=~/Library/Application\ Support/IBM\ Notes\ Data/
notes_backup_directory=~/Desktop/Notes\ Backup
backup_files=("$notes_data_directory/"*.{id,nsf} "$notes_data_directory/desktop8.ndk" "$notes_data_directory/archive" "$notes_data_directory/user.dic")
for i in "${backup_files[@]}"
do
cp -nipvR "$i" "$notes_backup_directory"
done
您可以使用大括号扩展来缩短 的定义backup_files
:
backup_files=("$notes_data_directory/"{*.{id,nsf},desktop8.ndk,archive,user.dic})
或者,在复制之前切换到目录。
notes_data_directory=~/Library/Application\ Support/IBM\ Notes\ Data/
notes_backup_directory=~/Desktop/Notes\ Backup
cd "$notes_data_directory"
shopt -s nullglob
backup_files=(*.{id,nsf} desktop8.nd[k] archiv[e] user.di[c])
for i in "${backup_files[@]}"
do
cp -nipvR "$i" "$notes_backup_directory"
done
另一种方法是在数组中存储模式列表,并使用不带引号的变量来扩展模式。请注意,大括号扩展需要在解析源代码行时进行,而通配符需要以未扩展的方式存储在数组中。如果模式中有任何空格,则需要用引号引起来;做到这一点有点棘手。我不推荐这种方法,它很难遵循。由于模式最终可能会或可能不会与文件匹配,但cp
至少需要一个源文件,因此您需要单独处理模式与零个文件匹配的情况。
notes_data_directory=~/Library/Application\ Support/IBM\ Notes\ Data/
notes_backup_directory=~/Desktop/Notes\ Backup
backup_patterns=(\*.{id,nsf} "desktop8.nd[k]" "archiv[e]" "user.di[c]")
# equivalent to backup_patterns=("*.id" "*.nsf" "desktop8.nd[k]" "archiv[e]" "user.di[c]")
shopt -s nullglob
for i in "${backup_patterns[@]}"
do
files=("$notes_data_directory/"$i)
if [[ ${#files[@]} -ne 0 ]]; then
cp -nipvR "${files[@]}" "$notes_backup_directory"
fi
done