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

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

CentOS 6.5 上的 Bash 4.2:

在我的文件中~/.bash_profile,我有很多别名,包括:

alias grep='grep -n --color=always'

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

$ grep -Re 'regex_here' *.py

然而,当我最近运行这个时:

$ find . -name '*.py' | xargs grep -E 'regex_here'

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

  • xargs读取环境中的别名?
  • 如果没有,有没有办法让它做到这一点?

答案1

使用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.

答案2

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

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

find . -name '*.py' | xargs bash -c '. ~/.bashrc; grep -E regex_here "$@"' _

输入正则表达式时要小心嵌套引用的复杂性。您可以通过将正则表达式作为参数传递给 shell 来简化您的生活。

find . -name '*.py' | xargs bash -c '. ~/.bashrc; grep -E "$0" "$@"' regex_here

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

find . -name '*.py' | xargs "${BASH_ALIASES[grep]}" regex_here

在 zsh 中:

find . -name '*.py' | xargs $aliases[grep] regex_here

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

find . -name '*.py' -print0 | xargs -0 "${BASH_ALIASES[grep]}" regex_here

或通过使用-exec

find . -name '*.py' -exec "${BASH_ALIASES[grep]}" regex_here {} +

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

grep regex_here **/*.py

这有一些限制:

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

另一种方法是按照 MariusMatutiae 的建议,使用进程替换

grep regex_here <(find . -name '*.py')

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

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

答案3

请将此作为另一种方法的演示,我在相关的文章中找不到该方法 那么问题:

您可以编写一个包装函数,xargs检查第一个参数是否是别名,如果是,则相应地扩展它。

这是一个完全可以做到这一点的代码,但不幸的是它需要Z壳因此不会与 bash 1:1 运行(坦白说,我不习惯巴什足以移植它):

xargs () {
        local expandalias
        if [[ $(which $1) =~ "alias" ]]; then
                expandalias=$(builtin alias $1) 
                expandalias="${${(s.'.)expandalias}[2]}"
        else
                expandalias=$1
        fi
        command xargs ${(z)expandalias} "${(z)@[2,-1]}"
}

证明它有效:

%别名 grep="grep -n"´                          # 包含匹配的行号
%查找 foo -name "*.p*" | xargs grep -E 测试
foo/bar.p0:151:#data=测试
foo/bar.p1:122:#data=test # 包含行号
%unalias grep
%查找 foo -name "*.p*" | xargs grep -E 测试
foo/bar.p0:#data=测试
foo/bar.p1:#data=test # 不包括行号
%

答案4

grep 将从环境变量 GREP_OPTIONS 中读取一组默认选项。如果你会放

 export GREP_OPTIONS='--line-number --color=always'

在你的 .bashrc 中,变量将被传递到子 shell,你将得到你期望的结果。

相关内容