bash 在 find + xargs grep 中得到退出代码 123

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26540813/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 11:38:06  来源:igfitidea点击:

Got exit code 123 in find + xargs grep

bashfindxargs

提问by Pan Ruochen

Here is my script

这是我的脚本

eval "find \( -type f -a \( -name '*.h' \) \) -print0" | xargs -0 -n100 grep  -f <(echo "stdio")
echo $?

Nothing is found and the exit code is 123.

什么也没找到,退出代码是 123。

If I modify it a little as follows

如果我修改一下如下

echo "stdio" >.P
eval "find \( -type f -a \( -name '*.h' \) \) -print0" | xargs -0 -n100 grep <.P
echo $?

Something is found but the exit code is still 123.

找到了一些东西,但退出代码仍然是 123。

So what is wrong?

那么有什么问题呢?

======================================================================

================================================== ====================

Actually I just want to write a small script to make find+xargs+grep easier. For exmaple,
xgrep -e PATTERN1 -e PATTERN2 ... *.c *.h
is to execute
find -name *.c -o -name *.h|xargs grep -f <(echo "$PATTEN1
$PATTERN2")

Use -f option instead of -e is to avoid troubles in escaping single or double quations within the patterns.

其实我只是想写一个小脚本,让 find+xargs+grep 更简单。例如,
xgrep -e PATTERN1 -e PATTERN2 ... *.c *.h
就是执行
find -name *.c -o -name *.h|xargs grep -f <(echo "$PATTEN1
$PATTERN2")

使用 -f 选项而不是 -e 是为了避免在模式中转义单引号或双引号时出现问题。

#!/bin/bash
#set -e -o pipefail

eval ARGV=($(getopt -l '' -o 'e:li' -- "$@")) || exit 1
for((i=0;i<${#ARGV[@]};i++)) {
    o="${ARGV[$i]}"
    case $o in
    -e)
        i=$((i+1));
        a="${ARGV[$i]}"
        if [ -n "$grep_patterns" ]; then
            grep_patterns="$grep_patterns"$'\n'
        fi
        grep_patterns="$grep_patterns$a"
        ;;
    -i)
        grep_options="$grep_options -i"
        ;;
    -l)
        grep_options="$grep_options -l"
        ;;
    --)
        i=$((i+1));
        break;;
    esac
}

for((;i<${#ARGV[@]};i++)) {
    if [ -n "$find_options" ]; then
        find_options="$find_options -o "
    fi
    find_options="${find_options}-name '${ARGV[$i]}'"
}

cmd="find \( -type f -a \( $find_options \) \) -print0"
eval "$cmd" | xargs -0 grep $grep_options -f <(echo "$grep_patterns")

回答by tripleee

123 means "any invocation exited with a non-zero status". So xargsran grepat least twice (because you fed it so many files that they would exceed the maximum command line length, which you limited to 100 files) and at least one of the invocations was on a set of files which contained no matches, which caused the exit code from grepto be nonzero (failure).

123 表示“任何以非零状态退出的调用”。所以至少xargs运行grep了两次(因为你给它提供了太多文件,它们会超过最大命令行长度,你限制为 100 个文件)并且至少有一个调用是在一组不包含匹配的文件上,这导致退出代码grep非零(失败)。

Perhaps you should explain what you are trying to accomplish. The evallooks superfluous and the double redirection is probably not accomplishing what you want (grep's standard input cannot simultaneously be connected to the pipe from evaland to .P).

也许您应该解释一下您要完成的工作。在eval看起来多余和双重定向可能不会完成你想要的东西(grep的标准输入不能同时从连接管eval.P)。

If you want to parametrize the first argument to grep, maybe do something like

如果您想将第一个参数参数grep化为 ,请执行以下操作

#!/bin/sh
find -type f -name '*.h' -print0 |
xargs -0 -n100 grep ""

... where you invoke this with e.g. stdioas the first argument.

...在那里你用 egstdio作为第一个参数来调用它。

(Notice also the much simplified parameters to find. You only have two predicates so there is no need to parenthesize anything, and then the -acan be dropped, too.)

(还要注意 . 的简化得多的参数find。您只有两个谓词,因此无需括号任何内容,然后-a也可以删除 。)

The exit code will still be 123 if there are grepinvocations which return zero matches. You can reduce the chances by omitting the -n 100(which hardly seems to serve any useful pupose anyway) but if you want to absolutely prevent it, you can feed the entire pipeline to | grep .which will report success if there was any output. (Or you could run xargson a wrapper which always returns success if the exit code from grepis either 0 or 1, but that is more complex, and you will then see "success" even in the case of zero matches.)

如果grep调用返回零匹配,退出代码仍将是 123 。您可以通过省略-n 100(无论如何似乎几乎没有任何有用的目的)来减少机会,但是如果您想绝对防止它,您可以提供整个管道,| grep .如果有任何输出,它将报告成功。(或者xargs,如果退出代码grep为 0 或 1,则您可以在始终返回成功的包装器上运行,但这更复杂,即使在零匹配的情况下,您也会看到“成功”。)

回答by tripleee

I'm posting this as a separate answer in response to your recent edit. I don't particularly want to change my existing answer, which already addresses the fundamental question.

为了回应您最近的编辑,我将此作为单独的答案发布。我并不特别想改变我现有的答案,它已经解决了基本问题。

Your script is unfortunately a classic example of the problem described in http://mywiki.wooledge.org/BashFAQ/050: "I am trying to put my command in a variable, but"...

不幸的是,您的脚本是http://mywiki.wooledge.org/BashFAQ/050 中描述的问题的经典示例:“我试图将我的命令放入变量中,但是”...

The short version is "don't do that". The long version is, try to use arrays, and avoid variables where they are not absolutely necessary. Here is an attempt at refactoring your tool along those lines.

简短的版本是“不要那样做”。长版本是,尽量使用数组,并避免使用不是绝对必要的变量。这是沿着这些方向重构您的工具的尝试。

#!/bin/bash
#set -e -o pipefail

grep_patterns=( )
grep_options=( )

eval ARGV=($(getopt -l '' -o 'e:li' -- "$@")) || exit 1
for((i=0;i<${#ARGV[@]};i++)) {
    case ${ARGV[$i]} in
    -e) i=$((i+1))
        grep_patterns+=("-e" "${ARGV[$i]}") ;;
    -i | -l)
        grep_options+=("${ARGV[$i]}") ;;
    --) i=$((i+1));
        break;;
    esac
}

find_options=("${ARGV[@]:$i}")

find -type f -a \( "${find_options[@]}" \) -print0 |
xargs -0 grep "${grep_options[@]}" "${grep_patterns[@]}"

I'm not sure passing multiple -eoptions to grepis supported everywhere, but it works fine with GNU grepand simplifies things to my mind.

我不确定在任何地方都支持传递多个-e选项grep,但它与 GNU 一起工作得很好,grep并且简化了我的想法。