让 xargs 使用别名而不是二进制

Mat*_*DMo 17 bash xargs alias

CentOS 6.5 上的 Bash 4.2:

在我的~/.bash_profile我有一堆别名,包括:

alias grep='grep -n --color=always'
Run Code Online (Sandbox Code Playgroud)

这样我就可以在运行时自动获得颜色突出显示和打印行号grep。如果我运行以下命令,突出显示按预期工作:

$ grep -Re 'regex_here' *.py
Run Code Online (Sandbox Code Playgroud)

但是,当我最近运行它时:

$ find . -name '*.py' | xargs grep -E 'regex_here'
Run Code Online (Sandbox Code Playgroud)

结果没有突出显示,也没有打印行号,迫使我返回并明确添加-n --color=alwaysgrep命令中。

  • 难道xargs没有读过环境别名?
  • 如果没有,有没有办法让它做到这一点?

1.6*_*803 17

alias xargs='xargs '

alias: alias [-p] [name[=value] ... ]
(snip)
A trailing space in VALUE causes the next word to be checked for
alias substitution when the alias is expanded.
Run Code Online (Sandbox Code Playgroud)


Gil*_*il' 14

别名在定义它的 shell 内部。它对其他进程不可见。外壳函数也是如此。xargs是一个单独的应用程序,它不是外壳,因此没有别名或函数的概念。

您可以让 xargs 调用 shell 而不是grep直接调用。然而,仅仅调用 shell 是不够的,您还必须在该 shell 中定义别名。如果别名在您的 中定义.bashrc,您可以获取该文件;但是,这可能不起作用,您.bashrc在非交互式 shell 中执行其他没有意义的任务。

find . -name '*.py' | xargs bash -c '. ~/.bashrc; grep -E regex_here "$@"' _
Run Code Online (Sandbox Code Playgroud)

键入正则表达式时,请注意嵌套引用的复杂性。您可以通过将正则表达式作为参数传递给 shell 来简化您的生活。

find . -name '*.py' | xargs bash -c '. ~/.bashrc; grep -E "$0" "$@"' regex_here
Run Code Online (Sandbox Code Playgroud)

您可以显式执行别名查找。然后xargs会看到grep -n --color=always

find . -name '*.py' | xargs "${BASH_ALIASES[grep]}" regex_here
Run Code Online (Sandbox Code Playgroud)

在 zsh 中:

find . -name '*.py' | xargs $aliases[grep] regex_here
Run Code Online (Sandbox Code Playgroud)

顺便说一句,请注意find … | xargs … 包含空格(除其他外)的文件名的中断。您可以通过更改为空分隔记录来解决此问题:

find . -name '*.py' -print0 | xargs -0 "${BASH_ALIASES[grep]}" regex_here
Run Code Online (Sandbox Code Playgroud)

或使用-exec

find . -name '*.py' -exec "${BASH_ALIASES[grep]}" regex_here {} +
Run Code Online (Sandbox Code Playgroud)

find您可以完全在 shell 内完成所有操作,而不是调用。glob 模式**/递归地遍历目录。在 bash 中,您需要先运行shopt -s globstar以启用此 glob 模式。

grep regex_here **/*.py
Run Code Online (Sandbox Code Playgroud)

这有一些限制:

  • 如果很多文件匹配(或者如果它们有很长的路径),命令可能会失败,因为它超过了最大命令行长度。
  • 在 bash ?4.2 中(但不是在更新的版本中,也不是在 ksh 或 zsh 中),**/递归到目录的符号链接。

另一种方法是使用进程替换,正如 MariusMatutiae 所建议的那样

grep regex_here <(find . -name '*.py')
Run Code Online (Sandbox Code Playgroud)

这在**/不适用时很有用:对于复杂的find表达式,或者在 bash ?4.2 中,当您不想在符号链接下递归时。请注意,这会中断包含空格的文件名;一种解决方法是设置IFS和禁用 globbing,但它开始变得有点复杂:

(IFS=$'\n'; set -f; grep regex_here <(find . -name '*.py') )
Run Code Online (Sandbox Code Playgroud)