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

时间:2014-10-24 03:01:58

标签: bash find xargs

这是我的剧本

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

找不到任何内容,退出代码为123。

如果我稍微修改一下

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

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

那有什么不对?

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

实际上我只想编写一个小脚本来使find + xargs + grep更容易。例如,
xgrep -e PATTERN1 -e PATTERN2 ... * .c * .h
是执行
find -name * .c -o -name * .h | xargs grep -f&lt;(echo&#34; $ PATTEN1
$ PATTERN2&#34)

使用-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")

2 个答案:

答案 0 :(得分:4)

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

也许你应该解释一下你想要完成的事情。 eval看起来多余,双重定向可能无法完成您想要的任务(grep的标准输入无法同时连接到eval和{{1}的管道}})。

如果要将第一个参数参数化为.P,可能会执行类似

的操作
grep

...你用例如调用它的地方#!/bin/sh find -type f -name '*.h' -print0 | xargs -0 -n100 grep "$1" 作为第一个参数。

(另请注意stdio的简化参数。您只有两个谓词,因此不需要对任何内容进行括号,然后也可以删除find。)

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

答案 1 :(得分:1)

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

遗憾的是,您的脚本是http://mywiki.wooledge.org/BashFAQ/050中描述的问题的典型示例:“我正在尝试将命令放在变量中,但是......”

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

#!/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[@]}"

我不确定将-e多个grep选项传递给grep,但它适用于GNU {{1}},并简化了我的想法。