Bearbeiten

Bearbeiten

Ich kann den folgenden Befehl in Bash ohne Fehler ausführen:

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

Ich habe eine Funktion in .bash_aliases geschrieben, um diesen Befehl schnell auszuführen:

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

Die Funktion gibt jedoch einen Fehler aus:

find: paths must precede expression

Wenn ich die letzte Zeile in ändere echo "${find_cmd[@]}", wird genau der gleiche Befehl wie oben gedruckt:

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

Ich verstehe nicht, warum es funktioniert, wenn es in der Konsole ausgeführt wird, aber fehlschlägt, wenn es innerhalb einer Funktion ausgeführt wird.

Auch wenn ich die Funktion auf den Befehl vereinfache, funktioniert es:

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

Ich bearbeite .bash_aliases in Notepad++, habe aber sichergestellt, dass die Zeilenenden im Unix-Format sind.

Bearbeiten

Gemäß F. Hauris Rat unten habe ich das Debuggen aktiviert. Offensichtlich ist dies der Befehl, der tatsächlich ausgeführt wird:

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

Ich bin mir nicht sicher, was ich mit dieser Information anfangen soll. Das Entfernen des Escape-Zeichens vor den Klammern führt zu einem anderen Fehler:

find: missing argument to -exec

Antwort1

Tipp: Ausführen, set -xum den Ablaufverfolgungsmodus zu aktivieren. Bash druckt jeden Befehl aus, bevor er ausgeführt wird. Ausführen, set +xum den Ablaufverfolgungsmodus zu deaktivieren.

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

Beachten Sie, dass das letzte Argument to statt findist . Sie haben das gleiche Problem mit den öffnenden und schließenden Klammern. In Ihrer Quelle haben Sie das Semikolon zweimal zitiert. Ändern Sie\;;

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

Zu

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

oder

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

Außerdem -name \'*.$file_type\'hat es falsche Anführungszeichen – Sie suchen nach Dateien, deren Name mit einem einfachen Anführungszeichen beginnt und endet. Machen Sie Folgendes -name "*.$file_type"(es *muss in Anführungszeichen gesetzt werden, falls es im aktuellen Verzeichnis übereinstimmende Dateien gibt, und Variablenerweiterungen sollten in doppelten Anführungszeichen stehen, es sei denn, Sie wissen, warum Sie die doppelten Anführungszeichen weglassen müssen).

Antwort2

LäuftBefehl mitArrays

Lassen Sie versuchen:

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

Wow, da gibt es eine Menge Ausgabe ...

Na dann:

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

Hmm ... Sie scheinen identisch zu sein!

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

Ok, zum Schluss:

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

während

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

Meine voll laufende Version:

Beispiel für die Verwendung eines Arrays als Befehl

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

Pflegeerste Zeichen im Abschnitt „Von <<-EOUsagebis“EOUsage musseine Tabellierung sein! Sie können dieses Skript herunterladenDortoder als .txt:Dort.

Notiz:Für die Funktion könnten (oder müssen) einige grepArgumente angegeben werden search:

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

verwandte Informationen