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 -x
um den Ablaufverfolgungsmodus zu aktivieren. Bash druckt jeden Befehl aus, bevor er ausgeführt wird. Ausführen, set +x
um den Ablaufverfolgungsmodus zu deaktivieren.
+ find . '\(' '\)' -exec grep -IH needle '{}' '\;'
Beachten Sie, dass das letzte Argument to statt find
ist . 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äuftSchlagBefehl 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 <<-EOUsage
bis“EOUsage
musseine Tabellierung sein! Sie können dieses Skript herunterladenDortoder als .txt
:Dort.
Notiz:Für die Funktion könnten (oder müssen) einige grep
Argumente angegeben werden search
:
search -t txt -Il /tmp/path /home/user 'invoice\|bill'