简体   繁体   English

.bashrc中的别名失败,而命令行成功

[英]An alias in .bashrc fails vs command line succeeds

I run this from a user's home dir to show me the most recent files while omitting the shell profile files: 我从用户的主目录运行此文件,以显示最新文件,同时省略了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;

This works, but just not as well when placed in my .bashrc as an alias: 这有效,但是放在我的.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;'

In the image you see the results without doing any filtering, followed by the desired result using grep -v for filtering which works on command line. 在图像中,您看到的结果没有进行任何过滤,然后使用grep -v进行过滤就可以在命令行上获得所需的结果。 Then final result - only partially succeeds in weeding out those files. 然后是最终结果-仅部分成功清除了这些文件。

I have tried using bash_ and [b]ash. 我试过使用bash_和[b] ash。 Not even bas (which fails to even get .basin) work ?!? 甚至bas(甚至无法获得.basin)都无法工作?!? And also I can use macs or acs AND still get the .emacs omitted so obviously the syntax in my alias is not respecting the /. 而且我也可以使用macs或acs并仍然省略.emacs,因此显然别名中的语法不遵守/。 either. 要么。 Not a problem with reserved words as I originally thought. 就像我最初想的那样,保留字不是问题。

终端截图

I DO get the expected results if I place my original command as is in a file and then use the alias that way: alias recentfiles='. 怎么 ,如果我把我原来的命令是在一个文件中,然后使用别名的方式得到预期的结果: 别名recentfiles =”。 /root/mycommands/recentfiles' / root / mycommands / recentfiles'

Can someone explain or point me to a reference to understand what is at play here? 有人可以解释或指点我一下以了解此处的作用吗? I wouldn't know what phrase with the proper terms to search on. 我不知道要搜索带有适当术语的短语。

This should fix your problems: 这应该可以解决您的问题:

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;'

The issue is with grep -P , where -P makes it use the perl regular expressions. 问题在于grep -P ,其中-P使它使用perl正则表达式。 In perl there is no need to use \\ in grouping. 在perl中,无需在分组中使用\\。 So (bash|emacs|...) instead of \\(bash|emacs|...\\) . 因此(bash|emacs|...)代替\\(bash|emacs|...\\) I really doubt it worked outside of .bashrc, unless you have some alias for grep which make it behave differently outside of .bashrc . 我真的怀疑它是否可以在.bashrc之外运行,除非您为grep设置了别名,从而使它在.bashrc之外的行为有所不同。

As other have said in the comments, your filtering is inefficient. 正如其他人在评论中所说,您的过滤效率很低。 Better rewrite your command with: 最好用以下命令重写命令:

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;

This way it will not waste time searching files inside .emacs.d/ or inside .kde/ , and will immediately prune the search. 这样,它不会浪费时间在.emacs.d/.kde/搜索文件,并且会立即修剪搜索。 Also, xargs -0 ls -l is so much shorter and clearer than the while loop. 同样, xargs -0 ls -l比while循环更短,更清晰。

To avoid issues with filenames that contain newlines, better use \\0 characters, that are never part of a file name: 为避免包含换行符的文件名出现问题,请更好地使用\\ 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

Part 1: Fixing The Issue 第1部分:解决问题

Use a function instead. 改用一个函数。

There are several major issues with aliases: 别名有几个主要问题:

  • Because you pass your content to be string-prefixed inside quotes when creating an alias, it's parsed differently than it would be when typed directly at the command line. 由于在创建别名时将内容传递给使用引号括起来的字符串,因此,其解析方式与直接在命令行中键入时的解析方式不同。

  • Because an alias is simple prefix substitution, they don't have their own arguments ( $1 , $2 , etc); 因为别名是简单的前缀替换,所以它们没有自己的参数( $1$2等); they don't have a call stack; 他们没有调用堆栈; debugging mechanisms like PS4=':$BASH_SOURCE:$LINENO+'; set -x 调试机制,例如PS4=':$BASH_SOURCE:$LINENO+'; set -x PS4=':$BASH_SOURCE:$LINENO+'; set -x can't tell you which file code from an alias originated in; PS4=':$BASH_SOURCE:$LINENO+'; set -x无法告诉您别名源自哪个文件代码; etc. 等等

  • Aliases are an interactive feature; 别名是一种交互式功能。 POSIX doesn't mandate that shells support them at all, and they're turned off by default during script execution. POSIX完全不要求外壳程序支持它们,并且在脚本执行期间默认情况下将其关闭。

Functions solve all these problems. 函数解决了所有这些问题。

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 --
}

Note that I also made several other changes: 请注意,我还进行了其他一些更改:

  • Instead of using \\n as a separator, the above code uses \\0 . 上面的代码不是使用\\n作为分隔符,而是使用\\0 This is because newlines can be found in filenames; 这是因为可以在文件名中找到换行符。 a file that contained newlines in its name could look like any number of files, with any arbitrary sizes it wanted, to the rest of your pipeline. 在名称中包含换行符的文件在管道的其余部分可能看起来像任意数量的文件,并且具有任意大小。 (Unfortunately, POSIX doesn't require that sort and tail support newline delimiters, so the -z options used above are GNUisms). (遗憾的是,POSIX不需要sorttail支持换行符分隔符,因此,上面使用的-z选项是GNUisms)。
  • Instead of using grep -v to remove dotfiles, I used the -prune option to find . 我没有使用grep -v删除点文件,而是使用-prune选项来find This is particularly important for directories like .kde , since it stops find from spending the time and I/O bandwidth to recurse down directories for which you intend to throw the results away anyhow. 这对于.kde目录尤其重要,因为它find浪费时间和I / O带宽来递归查找您打算丢弃结果的目录。
  • For documentation of the importance of the IFS= and -r arguments used in the while read loop, see BashFAQ #1 . 有关在while read循环中使用的IFS=-r参数的重要性的文档,请参阅BashFAQ#1 Both of these improve behavior in presence of unusual filenames (clearing IFS prevents trailing whitespace from being stripped; passing -r prevents literal backslashes from being elided). 这两种方法都可以改善文件名异常情况下的行为(清除IFS可以防止删除尾随空格;传递-r可以避免省略反斜杠)。
  • Instead of grep -P -- a GNU extension which is only available if grep was compiled with libpcre support -- my first cut (prior to moving to find -prune ) switched to grep -E , which is adequately expressive, much more widely available, and lends itself to higher performance implementations . 代替grep -P (仅当grep在libpcre支持下进行编译时才可用的GNU扩展),我的第一个剪切(在移动到find -prune )切换到了grep -E ,它具有充分的表达能力,可以使用得多并适合更高性能的实现

Part 2: Explaining The Issue 第二部分:解释问题

Running your alias after set -x , we see: 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

By contrast, running the command it was intended to wrap, we see: 相比之下,运行要包装的命令,我们看到:

+ find ./ -type f -printf '%T@ %p\n'
+ grep -vP '/\.(bash|emacs|gtkrc|kde/|zshrc)'
+ sort -n
+ tail -10
+ cut -f2- '-d '
+ read EACH

In the command itself, there are no literal backslashes before ( and ) . 在命令本身中, ()之前没有文字反斜杠。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM