find comando gera erros quando dentro da função

1

Eu posso executar o seguinte comando no bash sem erros:

$ find /d/Code/Web/Development/Source/ \( -name '*.cs' -o -name '*.cshtml' \) -exec grep -IH UserProfileModel {} \;

Eu escrevi uma função em .bash_aliases para executar rapidamente este comando:

search() {
    local file_type file_types find_cmd opt OPTARG OPTIND or pattern usage

    usage="Usage: search [OPTION] ... PATTERN [FILE] ...
Search for PATTERN in each FILE.
Example: search -t c -t h 'hello world' /code/internal/dev/ /code/public/dev/

Output control:
  -t    limit results to files of type"

    if [[ $1 == --help ]]; then
        echo "$usage"
        return
    fi

    file_types=()
    while getopts ":t:" opt; do
        case $opt in
            t)
                file_types+=("$OPTARG")
                ;;
            ?)
                echo "$usage"
                return
                ;;
        esac
    done
    shift $((OPTIND-1))

    if (( $# == 0 )); then
        echo "$usage"
        return
    fi

    pattern=$1
    shift

    if (( $# == 0 )); then
        echo "$usage"
        return
    fi

    find_cmd=(find "$@" '\(')
    or=""
    for file_type in "${file_types[@]}"; do
        find_cmd+=($or -name \'*.$file_type\')
        or="-o"
    done
    find_cmd+=('\)' -exec grep -IH "$pattern" {} '\;')

    "${find_cmd[@]}"
}

No entanto, a função gera um erro:

find: paths must precede expression

Se eu alterar a última linha para echo "${find_cmd[@]}" , ele imprime exatamente o mesmo comando acima:

$ search -t cs -t cshtml UserProfileModel /d/Code/Web/Development/Source/
find /d/Code/Web/Development/Source/ \( -name '*.cs' -o -name '*.cshtml' \) -exec grep -IH UserProfileModel {} \;

Não entendo por que funcionaria quando executado no console, mas falha quando executado dentro de uma função.

Além disso, se eu simplificar a função apenas para o comando, ela funciona:

search() {
    find /d/Code/Web/Development/Source/ \( -name '*.cs' -o -name '*.cshtml' \) -exec grep -IH UserProfileModel {} \;
}

Estou editando o .bash_aliases no Notepad ++, mas verifiquei se os finais de linha são em formato Unix.

Editar

Por conselho de F. Hauri abaixo, eu ativei a depuração. Aparentemente este é o comando que está sendo executado:

find /d/Code/Web/Development/Source/ '\(' -name ''\''*.cs'\''' -o -name ''\''*.cshtml'\''' '\)' -exec grep -IH UserProfileModel '{}' '\;'

Não sei o que fazer com essa informação. Remover o caractere de escape antes dos parênteses faz com que ele lance um erro diferente:

find: missing argument to -exec
    
por David Kennedy 08.05.2014 / 00:28

2 respostas

1

Dica: execute set -x para ativar o modo de rastreamento. Bash imprime cada comando antes de executá-lo. Execute set +x para desativar o modo de rastreamento.

+ find . '\(' '\)' -exec grep -IH needle '{}' '\;'

Observe como o último argumento para find é \; em vez de ; . Você tem o mesmo problema com os parênteses de abertura e fechamento. Na sua fonte, você citou o ponto-e-vírgula duas vezes. Mudar

    find_cmd=(find "$@" '\(')
    …
    find_cmd+=('\)' -exec grep -IH "$pattern" {} '\;')

para

    find_cmd=(find "$@" '(')
    …
    find_cmd+=(')' -exec grep -IH "$pattern" {} ';')

ou

    find_cmd=(find "$@" \()
    …
    find_cmd+=(\) -exec grep -IH "$pattern" {} \;)

Além disso, -name \'*.$file_type\' tem aspas ruins - você está procurando arquivos cujo nome comece e termine com uma aspa simples. Faça este -name "*.$file_type" (o * precisa ser citado caso haja arquivos correspondentes no diretório atual, e as expansões de variáveis devem estar entre aspas duplas, a menos que você saiba por que precisa deixar as aspas duplas).

    
por 08.05.2014 / 05:54
1

Execução do comando usando arrays

Vamos tentar:

find /tmp \( -type f -o -type d \) -ls 

Uau, há muita saída ...

Bem, agora:

cmd_list=(find /tmp \()
cmd_list+=(-type f)
cmd_list+=(-o -type d)
cmd_list+=(\) -ls)
"${cmd_list[@]}"

Hum ... parece idêntico!

find /tmp \( -type f -o -type d \) -ls 2>/dev/null | md5sum
eb49dfe4f05a90797e444db119e0d9bd  -
"${cmd_list[@]}" 2>/dev/null| md5sum
eb49dfe4f05a90797e444db119e0d9bd  -

Ok, finalmente:

printf "%q " "${cmd_list[@]}";echo
find /tmp \( -type f -o -type d \) -ls 

enquanto

printf "%s " "${cmd_list[@]}";echo
find /tmp ( -type f -o -type d ) -ls

Minha versão completa:

Bem, por diversão, há uma versão pequena e rápida:

search() {
    local OPTIND=0 _o
    local -a _t
    while getopts 't:' _o ;do
        case $_o in
            t) _t+=(${_t+-o} -name \*.${OPTARG}) ;;
            *) echo "Usage: $0 [-t type[ -t type]] /path [/path.. /path] pattern";
               exit 1 ;;
        esac
    done
    find ${@:OPTIND:$#-$OPTIND} \( "${_t[@]}" \) -print0 |
        xargs -0 grep -lI ${@:$#}
}

Espero que isso possa ajudar você.

    
por 08.05.2014 / 00:35