Редактировать

Редактировать

Я могу выполнить следующую команду в bash без каких-либо ошибок:

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

Я написал функцию в .bash_aliases для быстрого запуска этой команды:

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

Однако функция выдает ошибку:

find: paths must precede expression

Если я изменю последнюю строку на echo "${find_cmd[@]}", она выведет точно такую ​​же команду, как и выше:

$ 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 {} \;

Я не понимаю, почему это работает при запуске в консоли, но не работает при запуске внутри функции.

Кроме того, если я упрощу функцию до одной команды, она будет работать:

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

Я редактирую .bash_aliases в Notepad++, но убедился, что окончания строк имеют формат Unix.

Редактировать

По совету Ф. Хаури ниже я включил отладку. Видимо, это команда, которая на самом деле выполняется:

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

Я не уверен, что делать с этой информацией. Удаление экранирующего символа перед скобками приводит к появлению другой ошибки:

find: missing argument to -exec

решение1

Совет: запустите set -x, чтобы включить режим трассировки. Bash выводит каждую команду перед ее выполнением. Запустите, set +xчтобы выключить режим трассировки.

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

Обратите внимание, что последний аргумент to findвместо \;. ;У вас та же проблема с открывающимися и закрывающимися скобками. В вашем источнике вы дважды заключили точку с запятой в кавычки. Изменить

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

к

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

или

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

Кроме того, -name \'*.$file_type\'имеет плохие кавычки — вы ищете файлы, имя которых начинается и заканчивается одинарной кавычкой. Сделайте это -name "*.$file_type"(необходимо *заключать в кавычки, если в текущем каталоге есть соответствующие файлы, а расширения переменных должны быть в двойных кавычках, если только вы не знаете, почему нужно опускать двойные кавычки).

решение2

Бегкоманда с использованиеммассивы

Давайте попробуем:

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

Ого, тут столько всего вышло...

Ну теперь:

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

Хм... Кажется, они идентичны!

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

Ну и наконец:

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

пока

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

Моя полная рабочая версия:

Пример использования массива в качестве команды

search() {
    local OPTIND=0 _o _usage _debug=false
    local -a _t _g _cmd=(find)
    read -rN9999 _usage <<-EOUsage
    Usage: $FUNCNAME [-a] [-d] [-i] [-I] [-H] [-l] [-t type [-t type]] \\ 
            /path/ [path2/ ../path3/] pattern
        -t .ext specifying an extension to search for
        -d      debug, will dump command variable before execution
        -[aiIHl] are 'grep' argument, see: man grep.
        many type and many path could be given but only one pattern.
    EOUsage
    while getopts 't:aiIHld' _o ;do
        case $_o in
        d) _debug=true ;;
        t) _t+=(${_t+-o} -name \*.${OPTARG}) ;;
        [aiIHl]) _g+=(-$_o) ;;
        *) echo "${_usage%$'\n'}" ; return 1 ;;
        esac
    done
    _cmd+=(${@:OPTIND:$#-$OPTIND} -type f)
    ((${#_t[@]})) && _cmd+=(\( "${_t[@]}" \))
    _cmd+=(-exec grep ${_g[@]} ${@:$#} {} +)
    $_debug && declare -p _cmd
    "${_cmd[@]}"
}

Заботапервые символы в части от <<-EOUsageдоEOUsage долженбыть табуляцией! Вы можете скачать этот скрипттамили как .txt:там.

Примечание:grepДля функционирования можно (или нужно) привести некоторые аргументы search:

search -t txt -Il /tmp/path /home/user 'invoice\|bill'

Связанный контент