Ada*_*dam 3 command-line scripts rm symbolic-link
要设置问题,请想象以下场景:
mkdir ~/temp
cd ~/
ln -s temp temporary
Run Code Online (Sandbox Code Playgroud)
rm -rf temporary, rm -f temporary, 和rm temporaryeach 将删除符号链接但离开目录~/temp/。
我有一个脚本,其中符号链接的名称很容易派生,但链接目录的名称不是。
有没有办法通过引用符号链接来删除目录,而不是从中解析目录的名称ls -od ~/temporary?
您可以使用以下命令:
rm -rf $(ls -l ~/temporary | awk '{print $NF}')
Run Code Online (Sandbox Code Playgroud)
如果你不想解析ls,那么你可以使用:
rm -rf $(file ~/temporary | awk '{print $NF}' | tr -d "\`\',")
Run Code Online (Sandbox Code Playgroud)
或简单:
rm -rf $(readlink ~/temporary)
Run Code Online (Sandbox Code Playgroud)
为了兼顾目录名和链接名中的空格,可以修改最后一条命令如下
rm -rf "$(readlink ~/"temporary with spaces")"
Run Code Online (Sandbox Code Playgroud)
使用rm -rf $(readlink temporary)有一些缺陷:如果目录名有空格会失败。尝试一下:
$ mkdir 'I have spaces'
$ ln -s 'I have spaces' test
$ ls -log
drwxr-xr-x 2 4096 Oct 31 19:19 I have spaces
lrwxrwxrwx 1 14 Oct 31 19:19 test -> I have spaces/
$ rm -rf $(readlink test)
$ ls -log
drwxr-xr-x 2 4096 Oct 31 19:19 I have spaces
lrwxrwxrwx 1 14 Oct 31 19:19 test -> I have spaces/
Run Code Online (Sandbox Code Playgroud)
如果目录I,have和spaces已经存在,则是危险的。
添加引号有助于:
$ rm -rf "$(readlink test)"
$ ls -log
lrwxrwxrwx 1 14 Oct 31 19:19 test -> I have spaces/
Run Code Online (Sandbox Code Playgroud)
(显示在 read 中,因为符号链接现在不指向任何内容)。
然而,仍然有失败的情况。看
$ mkdir $'a dir with a trailing newline\n'
$ ln -s $'a dir with a trailing newline\n' test
$ ls -log
drwxr-xr-x 2 4096 Oct 31 19:30 a dir with a trailing newline?
lrwxrwxrwx 1 31 Oct 31 19:30 test -> a dir with a trailing new line?
$ rm -rf "$(readlink test)"
$ ls -log
drwxr-xr-x 2 4096 Oct 31 19:30 a dir with a trailing newline?
lrwxrwxrwx 1 31 Oct 31 19:30 test -> a dir with a trailing new line?
Run Code Online (Sandbox Code Playgroud)
什么??????
那么,我能做什么?
如果你正在使用bash,一个好的策略如下:使用cd与-P选项,到cd到目录中的所有链接已经被提领后。看区别:
$ cd test
$ pwd
/home/gniourf/lalala/test
$ cd ..
$ cd -P test
$ pwd
/home/gniourf/lalala/a dir with a trailing newline
$ # Cool!
Run Code Online (Sandbox Code Playgroud)
键入help cd有关更多帮助cdbash的内建命令。你会读到它cd有另一个开关,即-L默认使用的开关。
怎么办?
好吧,策略可能是cd -P进入目录,然后使用它。问题是我无法删除我所在的目录(好吧,实际上我可以,请参阅这篇文章的底部)。尝试一下:
$ rm -rf .
rm: cannot remove directory: `.'
Run Code Online (Sandbox Code Playgroud)
而 sudo 在这里不会有太大帮助。
所以我可以cd -P在那个目录中,并将工作目录保存在一个变量中,然后回到我所在的位置。这是个好主意!但是你们如何“将当前的工作目录保存在一个变量中”?你怎么知道你在哪个目录?相信大家都知道这个pwd命令。(哦,我实际上在 2 分钟前使用过它)。但如果我这样做:
$ saved_dir=$(pwd)
Run Code Online (Sandbox Code Playgroud)
由于尾随换行符,我会遇到同样的麻烦。幸运的是,Bash 具有PWD自动包含工作目录的变量(如有必要,带有尾随换行符)。
好的,在这一点上,我想告诉你一些事情。当然,您如何知道变量的确切内容?echo是不足够的。看:
$ a="lalala " # with 4 trailing spaces
$ echo "$a"
lalala
$ # :(
Run Code Online (Sandbox Code Playgroud)
一个技巧是使用这个:
$ a="lalala "
$ echo "'$a'"
'lalala '
$ # :)
Run Code Online (Sandbox Code Playgroud)
凉爽的。但有时您将无法看到所有内容。一个好的策略是将 bash 的内置函数printf与其%q修饰符一起使用。看:
$ a="lalala "
$ printf '%q\n' "$a"
lalala\ \ \ \
$ # :)
Run Code Online (Sandbox Code Playgroud)
应用:
$ cd -P test
$ pwd
a dir with a trailing newline
$ printf '%q\n' "$PWD"
$'a dir with a trailing newline\n'
$ # :)
$ cd .. # let's go back where we were
Run Code Online (Sandbox Code Playgroud)
惊人的!
因此,删除此目录的策略如下:
$ cd -P test
$ saved_dir=$PWD
$ cd -
/home/gniourf/lalala
$ rm -rf "$PWD"
$ ls -log
lrwxrwxrwx 1 31 Oct 31 19:30 test -> a dir with a trailing new line?
$ # :)
Run Code Online (Sandbox Code Playgroud)
完毕!
现在,等等,那是cd -什么?嗯,这只是意味着:回到我在cd这里之前的地方。 :)
你想把它放在一个函数中吗?我们走吧:
rm_where_this_points_to() {
cd -P -- "$1" || { echo >&2 "Sorry, I couldn't go where \`$1' points to."; return 1; }
local saved_dir=$PWD
cd -
if [[ $saved_dir = $PWD ]]; then
echo &>2 "Oh dear, \`$1' points here exactly."
return 1
fi
rm -rfv -- "$saved_dir"
}
Run Code Online (Sandbox Code Playgroud)
完毕!
注意我是如何使用的--,以防万一 dir 以连字符开头,以免混淆cd和rm。或者,
rm_where_this_points_to() {
local here=$PWD
cd -P -- "$1" || { echo >&2 "Sorry, I couldn't go where \`$1' points to."; return 1; }
local saved_dir=$PWD
cd -- "$here"
if [[ "$saved_dir" = "$PWD" ]]; then
echo >&2 "Oh dear, \`$1' points here exactly."
return 1
fi
rm -rfv -- "$saved_dir"
}
Run Code Online (Sandbox Code Playgroud)
如果你不想cd -在函数内部使用。更好的是,bash这个变量OLDPWD包含你在最后一个之前所在的目录cd(这个变量用于cd -)。然后,您可以使用它来避免使用变量来保存位置(但这有点令人困惑):
rm_where_this_points_to() {
cd -P -- "$1" || { echo >&2 "Sorry, I couldn't go where \`$1' points to."; return 1; }
cd -- "$OLDPWD"
# At this point, OLDPWD is the directory to remove, not the one we're in!
if [[ "$OLDPWD" = "$PWD" ]]; then
echo >&2 "Oh dear, \`$1' points to here exactly."
return 1
fi
rm -rfv -- "$OLDPWD"
}
Run Code Online (Sandbox Code Playgroud)
此方法的一个副作用是它会更改您的目录堆栈(因此在执行此功能后,cd -将无法如您所愿)。
好吧,还有一件事我想告诉你。它实际上会printf与它的%q修饰符一起使用,也会使用可怕的、邪恶的eval命令。只是因为很好笑。这将具有不需要cd后退的优点。我们将从cd子shell调用。哦,你知道子壳不是吗?它是用括号括起来的可爱小东西。当子 shell 中发生某些事情时,父 shell 看不到它。看:
$ a=1
$ echo "$a"
1
$ ( a=42 ) # <--- in a subshell!
$ echo "$a"
1
$ # :)
Run Code Online (Sandbox Code Playgroud)
现在这也适用于cd:
$ pwd
/home/gniourf/lalala
$ ( cd ~ ; pwd )
/home/gniourf
$ pwd
/home/gniourf/lalala
$ # :)
Run Code Online (Sandbox Code Playgroud)
这就是为什么有时,在脚本中,您会看到在子 shell 中完成的事情,以免弄乱全局状态。例如,当与IFS. 这通常被认为是一种很好的做法。
回到我们的事情:我们将如何PWD在子 shell 中取回while的值,因为我刚刚向您展示了在子 shell 中设置的变量不能被父 shell 看到。这就是我们将使用printf它的%q修饰符的地方:
$ ( cd test; printf '%q' "$PWD" )
$'a dir with a trailing newline\n'
$ # :)
Run Code Online (Sandbox Code Playgroud)
现在,我们可以将子shell的标准输出放在一个变量中吗?当然,只需在子 shell 前面加上一个$as:
$ labas=$(cd test; printf '%q' "$PWD")
$ echo "$labas"
$'a dir with a trailing newline\n'
$ # :)
Run Code Online (Sandbox Code Playgroud)
(là-bas在法语中的意思是那边)。
但是我们要怎么处理这个愚蠢的字符串呢?很简单,我们将使用可怕的、邪恶的eval。真的,永远不要,永远,永远不要使用eval. 这不好。每次使用时上帝都会杀死一只小猫eval。真的,相信我。当然,除非你完全知道自己在做什么。除非您绝对确定您的数据已被清理。(你知道鲍比桌吗?)。但在这里,情况就是这样。printf的修饰符%q只是为我们做这件事!然后:
rm_where_this_points_to() {
local labas=$( cd -P -- "$1" && printf '%q' "$PWD" )
[[ -z "$labas" ]] && { echo >&2 "Couldn't go where \`$1' points to."; return 1; }
if [[ "$labas" = "$(printf '%q' "$PWD")" ]]; then
echo >&2 "Oh dear, \`$1' points here exactly."
return 1
fi
# eval and unquoted $labas, I know what I'm doing:
# $labas has been sanitized and comes directly from `printf '%q'`
eval "rm -rfv -- $labas"
}
Run Code Online (Sandbox Code Playgroud)
看,有一个未引用的$labal和一个eval!当你看到这个时,你应该在内心深处感到非常奇怪!(这就是我的情况)。这就是我添加评论的原因。所以以后我会意识到这很好。
不过,有一个警告。检查我们没有删除当前目录的部分存在缺陷,如下所示:
$ ln -s . wtf
$ rm_where_this_points_to wtf
Oh dear, `wtf' points here exactly.
$ # Good :) but look:
$ cd wtf
$ rm_where_this_points_to wtf
$ # Oh, it did it! :(
Run Code Online (Sandbox Code Playgroud)
为了解决这个问题,我们需要取消引用PWD,转义它,printf '%q'并将它与我们想要删除的取消引用和转义的东西进行比较。它只会使代码稍微复杂一些:
rm_where_this_points_to() {
local labas=$( cd -P -- "$1" && printf '%q' "$PWD" )
local ici=$( cd -P . && printf '%q' "$PWD" )
[[ -z "$labas" ]] && { echo >&2 "Couldn't go where \`$1' points to."; return 1; }
[[ -z "$ici" ]] && { echo >&2 "Something really weird happened: I can't dereference the current directory."; return 1; }
if [[ "$labas" = "$ici" ]]; then
echo >&2 "Oh dear, \`$1' points here exactly."
return 1
fi
# eval and unquoted $labas, I know what I'm doing:
# $labas has been sanitized and comes directly from `printf '%q'`
eval "rm -rfv -- $labas"
}
Run Code Online (Sandbox Code Playgroud)
(ICI意味着这里法文)。
这是我将使用的功能(直到我发现它仍然有一些缺陷......如果你发现任何缺陷,请在评论中告诉我)。
我之前说过一个很酷的解决方案是到cd -P我要删除的目录,然后使用 删除 if rm -rf .,但这不起作用。好吧,一个非常肮脏的方法是rm -rf -- "$PWD"改为。这太脏了,但很有趣:
rm_where_this_points_to_dirty() {
( cd -P -- "$1" && rm -rfv -- "$PWD" )
}
Run Code Online (Sandbox Code Playgroud)
完毕!
使用风险自负!!!
干杯!
在这篇文章中,您可能已经了解了以下内容之一:
cd -P与cd -L.$(...).PWD和OLDPWD.$'...'有一些很酷的效果。printf及其%q修饰符。--.eval. 绝不。| 归档时间: |
|
| 查看次数: |
5170 次 |
| 最近记录: |