我将 ZSH 作为我的默认 shell,并在其之上使用 OhMyZsh。有时我需要删除一些文件并重置一些权限/所有权,所以我使用它的别名。假设我有一个 PHP/Symfony 项目/home/parallels/Development/prj1
。有了下面的脚本,我就可以执行它,fixperms prj1
但是......
- 如果我执行该脚本一次并且它删除了内容,
$CURRENT_PROJECT/$PROJECT_CACHE
我最终会出现以下错误:
fixperms:4: no matches found: /home/parallels/Development/prj1/framework/var/cache/*
如果我删除它
*
末尾的$CURRENT_PROJECT/$PROJECT_CACHE/*
就会删除cache
我不想要的文件夹该文件
dev.log
永远不会被删除
我在这里缺少什么?
PROJECT=framework
PROJECT_VAR=$PROJECT/var
PROJECT_CACHE=$PROJECT_VAR/cache
PROJECT_LOG=var/log/dev.log
fixperms () {
if [ -n "$1" ]; then
CURRENT_PROJECT=$HOME/Development/"$1"
cd $CURRENT_PROJECT && sudo rm -rf $CURRENT_PROJECT/$PROJECT_CACHE/* && sudo rm -rf $CURRENT_PROJECT/$PROJECT_LOG && sudo chown -R "$(whoami)":root $CURRENT_PROJECT && sudo chmod -R 777 $CURRENT_PROJECT/$PROJECT_VAR
else
echo "Missing project param."
fi
}
注意:我不是 bash/shell 脚本专家,因此我们非常欢迎并提前感谢任何改进
答案1
当您链接所有操作时,&&
如果其中一个操作失败,那么该操作就会中断 - 并且该行之后的任何操作都不会被执行。不确定这是有意还是无意。
尝试例如:
echo A && false && echo B
echo A && true && echo B
因此,为什么当项目缓存失败dev.log
时永远不会被删除。该行rm
之后的任何内容都不会被执行。rm -rf $CURRENT_PROJECT/$PROJECT_CACHE/*
zsh
默认情况下关闭nullglob
,因此*
失败。
无论如何;
您可以先检查目录是否为空,然后再检查rm
。更简单的方法是简单地删除它并重新创建它。
setopt nullglob
或者可以选择unsetopt nullglob
之后。
其他注意事项:
您可能有使用大写变量的原因,但发现最好尽可能避免它。它可能会因环境变量、内置函数等而崩溃。
你应该多引用一些。
我假设$project_log
是一个文件,如果是的话-r
就没用了。
echo
/printf
错误到stderr
.
project=framework
project_var=$project/var
project_cache=$project_var/cache
project_log=var/log/dev.log
fixperms () {
if [ -n "$1" ]; then
current_project="$HOME/Development/$1"
# Change to project directory. Exit if it fails.
# The shell should give descriptive error.
pushd "$current_project" || exit 1
[ -e "./$project_cache/" ] && sudo rm -r "./$project_cache/"
sudo mkdir "./$project_cache"
[ -e "./$project_log" ] && sudo rm "./$project_log"
sudo chown -R "$(whoami):root" .
sudo chmod -R 777 "./$project_var"
# Restore directory we started from
popd
else
printf 'fixperms(): Missing argument: project\n' >&2
fi
}
您可能想要return 1
而不是exit 1
全部取决于您如何使用该功能......
无论如何,当我们检查其存在时,将-f
其删除。rm
答案2
sudo rm -rf $CURRENT_PROJECT/$PROJECT_CACHE/*
以下是该命令的执行方式(我只提到重要的部分):
- shell 扩展最后一个参数中的变量。结果是
/home/parallels/Development/prj1/framework/var/cache/*
。 - 最后一个参数包含通配符模式,因此它检查该模式是否与文件匹配。由于您的帐户没有列出 中文件的权限
/home/parallels/Development/prj1/framework/var/cache
,因此该模式不匹配任何文件(shell 无法区分“我可以确定没有匹配的文件”和“我无法确定是否有任何匹配的文件”)匹配文件”)。不匹配任何文件的通配符模式将被保留。 - shell
sudo
使用参数rm
,-rf
,执行程序/home/parallels/Development/prj1/framework/var/cache/*
。 sudo
rm
使用参数-rf
,执行/home/parallels/Development/prj1/framework/var/cache/*
。rm
删除指定的文件,即如果*
目录中有一个文件被调用/home/parallels/Development/prj1/framework/var/cache
,则该文件被删除(如果*
是一个目录,则其内容被递归删除)。
您需要安排具有读取目录权限的 shell 执行通配符扩展(或用于枚举要删除的文件的任何方法)。这意味着通配符扩展必须发生在 sudo 下运行的 shell 中,而不是在调用 sudo 的 shell 中。例如:
sudo sh -c 'rm -rf "$0"/*' "$CURRENT_PROJECT/$PROJECT_CACHE"
请注意扩展事物时所采取的谨慎措施。不起作用,因为变量未在内壳中定义(它将运行sudo sh -c 'rm -rf $CURRENT_PROJECT/$PROJECT_CACHE/*'
rm -rf //*
,这不是您应该尝试的)。仅当所涉及的变量值不包含空格等字符时才有效,因为这些值被解释为 shell 代码片段而不是文件名。sudo sh -c "rm -rf $CURRENT_PROJECT/$PROJECT_CACHE/*"
注意我不建议运行上面的命令,因为它很脆弱:如果路径不完全符合您的预期,您最终可能会删除系统上的任何内容,并且由于该命令以 root 身份运行,这可能会非常糟糕。相反,您应该仅使用必要的权限运行命令:
sudo -u parallelsowner sh -c 'rm -rf "$0"/*' "$CURRENT_PROJECT/$PROJECT_CACHE"
或者
sudo -g parallelsgroup sh -c 'rm -rf "$0"/*' "$CURRENT_PROJECT/$PROJECT_CACHE"
其中parallelsowner
是拥有相关目录及其子目录的用户,或者parallelsgroup
是对该目录及其所有子目录具有写访问权限的组。