.bashrc中的别名失败,而命令行成功
我从用户的home dir运行此命令,以显示最新的文件,同时忽略shell配置文件:.bashrc中的别名失败,而命令行成功,bash,shell,alias,Bash,Shell,Alias,我从用户的home dir运行此命令,以显示最新的文件,同时忽略shell配置文件: find ./ -type f -printf "%T@ %p\n"|grep -vP "/\.(bash|emacs|gtkrc|kde/|zshrc)" |sort -n| tail -10|cut -f2- -d" "|while read EACH; do ls -l "$EACH"; done; 这是可行的,但如果将其作为别名放置在my.bashrc中,效果就不一样了: alias recentfi
find ./ -type f -printf "%T@ %p\n"|grep -vP "/\.(bash|emacs|gtkrc|kde/|zshrc)" |sort -n| tail -10|cut -f2- -d" "|while read EACH; do ls -l "$EACH"; done;
这是可行的,但如果将其作为别名放置在my.bashrc中,效果就不一样了:
alias recentfiles='find ./ -type f -printf "%T@ %p\n"|grep -vP "/\.\(bash|emacs|gtkrc|kde/|zshrc\)"|sort -n| tail -10|cut -f2- -d" "|while read EACH; do ls -l "$EACH"; done;'
在图像中,您可以看到不进行任何过滤的结果,然后是使用grep-v
进行过滤的所需结果,该过滤在命令行上运行。最后的结果是,仅部分成功地清除了这些文件
我试过使用bash和[b]ash。甚至连bas(它甚至不能得到.basin)的工作都没有?!?我也可以使用macs或acs,但仍然可以省略.emacs,因此很明显,我的别名中的语法不尊重/。任何一个这不是我最初认为的保留字的问题
如果我将原始命令原样放在文件中,然后以这种方式使用别名,我确实会得到预期的结果:
别名recentfiles='/root/myCommand/recentfiles'
有人能给我解释一下,或者给我指一个参考资料,让我明白这里在起什么作用吗?我不知道要搜索哪个短语的正确术语。这应该可以解决您的问题:
alias recentfiles='find ./ -type f -printf "%T@ %p\n"|grep -vP "/\.(bash|emacs|gtkrc|kde/|zshrc)"|sort -n| tail -10|cut -f2- -d" "|while read EACH; do ls -l "$EACH"; done;'
问题在于grep-p
,其中-p使用perl正则表达式。在perl中,分组时不需要使用\。所以(bash | emacs |…)
而不是\(bash | emacs |…\)
。我真的怀疑它是否在.bashrc之外工作,除非你有grep
的别名,这使得它在.bashrc
之外的行为有所不同
正如其他人在评论中所说,您的过滤效率低下。最好使用以下命令重写命令:
find ./ \( -name ".bash*" -o -name ".emacs*" -o -name .gtkrc -o -name .kde -o -name .zshrc \) -prune -o \( -type f -printf "%T@ %p\n" \) |sort -n| tail -10|cut -f2- -d" "| tr "\n" "\0" | xargs -0 ls -l;
这样,它就不会浪费时间在.emacs.d/
或.kde/
内搜索文件,并且会立即删除搜索。而且,xargs-0ls-l
比while循环更短、更清晰
要避免包含换行符的文件名出现问题,最好使用\0个字符,这些字符永远不会是文件名的一部分:
find ./ \( -name ".bash*" -o -name .emacs -o -name .gtkrc -o -name .kde -o -name .zshrc \) -prune -o \( -type f -printf "%T@ %p\0" \) |sort -n -z | tail -z -n -10| cut -z -f2- -d" " | xargs -0 ls -l
第1部分:解决问题
改用函数。
别名有几个主要问题:
- 由于在创建别名时将内容传递给引号内的字符串前缀,因此其解析方式与直接在命令行中键入时不同
- 因为别名是简单的前缀替换,所以它们没有自己的参数(
,$1
,等等);它们没有调用堆栈;调试机制,如$2
无法告诉您别名中的文件代码来自哪个文件;等等PS4=':$BASH_SOURCE:$LINENO+;set-x
- 别名是一种交互式功能;POSIX根本不要求shell支持它们,并且在脚本执行过程中默认关闭它们
recentfiles() {
find ./ \
'(' -name '.bash*' -o -name '.emacs*' -o -name .gtkrc -o -name .kde -o -name .zshrc ')' -prune \
-o -type f -printf "%T@ %p\0" |
sort -nz |
tail -z -n -10 |
while read -d' ' _ && IFS= read -r -d '' file; do
printf '%s\0' "$file"
done |
xargs -0 ls -ld --
}
请注意,我还做了一些其他更改:
- 上面的代码没有使用
作为分隔符,而是使用\n
。这是因为可以在文件名中找到换行符;对于管道的其余部分来说,名称中包含换行符的文件可以像任意数量的文件一样,具有任意大小。(不幸的是,POSIX不要求\0
和sort
支持换行分隔符,因此上面使用的tail
选项是GNUISM)-z
- 我没有使用
删除点文件,而是使用grep-v
选项-prune
。这对于像查找
这样的目录尤其重要,因为它阻止了.kde
花费时间和I/O带宽来递归您打算丢弃结果的目录find
- 有关读取时
循环中使用的
和IFS=
参数重要性的文档,请参阅。这两种方法都可以改善出现异常文件名时的行为(清除IFS可以防止删除尾随空格;传递-r
可以避免删除文字反斜杠)-r
- 而不是
——只有在grep-P
使用libpcre支持编译时才可用的GNU扩展——我的第一个剪切(在移动到grep
之前)切换到find-prune
,它具有充分的表达能力,可用性更广,并且更具可扩展性grep-E
第二部分:解释问题 在
set-x
之后运行别名,我们会看到:
+ find ./ -type f -printf '%T@ %p\n'
+ grep -vP '/\.\(bash|emacs|gtkrc|kde/|zshrc\)'
+ sort -n
+ tail -10
+ cut -f2- '-d '
+ read EACH
相比之下,运行它要包装的命令时,我们看到:
+ find ./ -type f -printf '%T@ %p\n'
+ grep -vP '/\.(bash|emacs|gtkrc|kde/|zshrc)'
+ sort -n
+ tail -10
+ cut -f2- '-d '
+ read EACH
在命令本身中,
(
和)
之前没有文字反斜杠,为什么不告诉查找
来过滤掉这些反斜杠呢?改用函数;)创建别名意味着shell将扫描该命令行,并对单引号和dbl引号的字符串进行处理。在通过处理激活cmd字符串之后,很难理解它处于什么“状态”(使用了什么引号和转义字符,哪些仍然存在)。修复别名真的不值得花时间,最好使用函数,它们是可调试的,它们可以接受运行时参数,并且不必欺骗shell解析器来让它们工作。好的,祝你好运。使用\(-name.bash-o-name.emacs-o…\)
在find
命令中,而不是grep
。当您编写“在图像中,您看到了”时,我假设您试图发布图像,但不知何故无法发布。这很好,直接复制粘贴终端输出要好得多。xargs ls-l
会对带有空格的文件名产生错误行为:如果您有一个名为foo bar
的文件,您将运行ls-l“foo”“bar”
。至少应使用xargs-d$'\n'ls-l
来更正带有空格或引号的文件名;最好重写管道的其余部分,以使用NUL分隔符,以便对具有文字换行符的文件名也是正确的。Y