Ich möchte eines meiner Skripte kürzen und habe keine Idee, aber ich weiß nicht, wie ich das machen soll. Ich habe einen Codeabschnitt wie diesen:
COMMAND="find /etc -type "
case $1:
"directory") $COMMAND d
;;
esac
Dies ist natürlich die Kurzversion :) Und jetzt möchte ich es abkürzen können, um nicht überall dieses $COMMAND hinzuschreiben, also hätte ich gerne so etwas:
$COMMAND <the case statement return value>
aber ich möchte keine Variable verwenden, um das Ergebnis des Falls zu speichern.
Danke :) Hoffe du hast verstanden was ich will :D
EDIT 1: Es ist möglich, eine Funktion zu erstellen und den Find-Parameter als $1 zu übergeben, wie von Serg angegeben. Wenn ich es nun ohne die Funktion machen wollte, gibt es sicher einen Weg :D Nicht, dass Serg es nicht gelöst hätte, ich bin nur neugierig :D
Antwort1
Es gibt mehrere Möglichkeiten, den Code zu vereinfachen. Nachfolgend sind die Lösungen nach der Menge des Codes sortiert:
printf
und Parametersubstitution (keine Fehlerprüfung)xargs
und Parametersubstitution (keine Fehlerprüfung)find
und nur Parameterersetzung (keine Fehlerprüfung)- Fall-Through-Case-Struktur (löst das Problem der Fehlerprüfung)
- Testlogik, xargs und Parametersubstitution
- Bash-Funktion
- Array, For-Schleife, Test und &&
printf
und ParametersubstitutionslösungEine wenig bekannte Eigenschaft der
printf
Funktion ist, dass beim Aufrufprintf "%c" someString
nur dieErsteZeichen dieser Zeichenfolge. So können wir die Verwendung von Case-Anweisungen mit Parametererweiterung vermeiden undprintf
wie folgt vorgehen:xieerqi:$ cat someScript.sh #!/bin/bash find /etc -type $(printf "%c" $1 )
jetzt ausführen:
xieerqi:$ sudo ./someScript.sh directory | head [sudo] password for xieerqi: /etc /etc/logrotate.d /etc/apm /etc/apm/event.d /etc/apm/scripts.d /etc/apm/resume.d /etc/apm/suspend.d /etc/speech-dispatcher /etc/speech-dispatcher/modules /etc/speech-dispatcher/clients
Eine Einschränkung besteht hier darin, dass wir zum Aufrufen einen Prozess aufzweigen
printf
, was durch die Funktionslösung vermieden wird – Funktion und Case-Struktur sind alles native Bash-Tools.xargs
und ParametersubstitutionMithilfe der Parametersubstitution von bash können wir einen Teilstring aus einer Variablen abschneiden (
${VAR:0:3}
gibt beispielsweise die ersten 3 Zeichen vonVAR
); in diesem Fall möchten wir das erste Zeichen für einen Typdirectory
oderfile
. Dann können wirxargs
dies als Parameter an übergebenfind
echo ${1:0:1} | xargs -I {} find /etc -type {}
Die
find
Manpage erwähnt, dass-type
es für Flag unter Solaris etwas gibt, das als Door-Datei bekannt ist und durch den Großbuchstaben dargestellt wirdD
. Da wir aber Linux verwenden, können wir sagen, dass es sich hierbei um eine Einschränkung handelt, die man vernünftigerweise ignorieren kann.Es gibt jedoch eine weitere Gefahr in diesem Code - wenn ein Benutzer
flower
als$1
Parameter eingibt, wird immer noch nach gesucht-type f
, da wir das erste Zeichen einer beliebigen Zeichenfolge nehmen … Mit anderen Worten,Es gibt keine Fehlerprüfung.find
mit ParametererweiterungWenn wir die Parametererweiterung noch weiter vorantreiben, können wir Folgendes tun:
find /etc -type ${1:0:1}
Im Grunde ein Einzeiler mit
find
Befehl und einer Teilzeichenfolge der$1
Variablen.Auch keine Fehlerprüfung.Fall-Through-Fallstruktur
Das große Problem bei den letzten drei Methoden ist die Fehlerprüfung. Sie sind gut, wenn Sie darauf vertrauen, dass der Benutzer kein Dummkopf ist oder nur Code für sich selbst schreibt. In Java ist es nun möglich, eine
switch
Anweisung zu schreiben, die denselben Befehl in mehreren Fällen ausführt, wenn Sie denbreak
Befehl einfach weglassen.bash
Dies kann auch mit;&
dem Terminator erfolgen. Zitat ausman bash
Die Verwendung
;&
anstelle von;;
bewirkt, dass die Ausführung mit der Liste fortgesetzt wird, die dem nächsten Mustersatz zugeordnet ist.Wir müssen nur die Typen testen, wie „Verzeichnis“, „Datei“, „Block“ usw., und dann die Parametersubstitution verwenden, um das erste Zeichen abzuschneiden. So
#!/bin/bash case "$1" in "directory") ;& "file");& "block") find /etc -type ${1:0:1} ;; *) exit 1 ;; esac
5.Testlogik, xargs und Parametersubstitution
Basic idea here is that we're sending $1 variable through pipe to `xargs`, which in turn substitutes it into test (again square brackets). `xargs` in turn builds the actual command that runs by replacing `{}` with whatever was passed to `xargs`. As for test command, it's simple or statement, `EXPRESSION -o EXPRESSION ` , where we test if string $1 is equal to either "file" or "directory string"
echo "$1" | xargs -I {} [ "{}" = "file" -o "{}" = "directory" ] \
&& find /etc -type ${1:0:1}
`xargs` is really useful when you need to process multiple argumens with the same command. Considering that in this case we only have one argument that needs to be processed with the same command , this can be simplified to
[ "$1" = "file" -o "$1" = "directory" ] && find /etc -type ${1:0:1}
Of course the big limitation is that if you test for more than one type, you need longer `[ EXPR1 -o EXPR2 ]` structure with multiple `-o` parts.
Funktionslösung
find
Befehl kann in eine Funktion eingefügt werden, die dann mit Positionsparametern aufgerufen werden kann.Zum Beispiel:
function findStuff { find /etc -type "$1" }
Hier ist eine kleine Demo. Beachten Sie, dass ich verwende,
sudo
weil für viele Dateien/etc
normale Benutzer keine Leseberechtigung habenxieerqi:$ sudo ./someScript.sh directory | head [sudo] password for xieerqi: /etc /etc/logrotate.d /etc/apm /etc/apm/event.d /etc/apm/scripts.d /etc/apm/resume.d /etc/apm/suspend.d /etc/speech-dispatcher /etc/speech-dispatcher/modules /etc/speech-dispatcher/clients xieerqi:$ sudo ./someScript.sh file | head [sudo] password for xieerqi: /etc/hosts.deny /etc/logrotate.d/speech-dispatcher /etc/logrotate.d/pm-utils /etc/logrotate.d/rsyslog /etc/logrotate.d/yate /etc/logrotate.d/apport /etc/logrotate.d/apt /etc/logrotate.d/consolekit /etc/logrotate.d/fail2ban /etc/logrotate.d/cups-daemon xieerqi:$ cat someScript.sh #!/bin/bash function findStuff { find /etc -type "$1" } case "$1" in "directory")findStuff d ;; "file") findStuff f;; esac
Array, For-Schleife, Test und &&
Grundidee hier: Benutzereingabe mit einer Liste abgleichen und bei Übereinstimmung etwas tun. Wir erstellen ein Array von Elementen, nach denen gesucht werden soll, haben eine Testbedingung (eckige Klammern sind übrigens Aliasnamen für
test
Befehle) und führen einfach eine Schleife aus, um die Variable $1 zu testen.&&
Der Operator ermöglicht die Ausführung des Befehls genau dann, wenn das, was links davon steht,&&
erfolgreich war. Wenn wir also eine Zeichenfolge im Array gefunden haben, führen wir den Befehl find aus. Das ${1:0:1} wurde in den vorherigen Beispielen besprochen – eine Parametererweiterung, die das erste Zeichen von unserem übereinstimmenden Typ abschneidet. Diese Lösung bietet also eine Fehlerprüfung und eine ganze Menge Code, der in nur 3 Zeilen (4, wenn Sie#!
Zeilen einschließen) gepackt ist.#!/bin/bash array=("file" "directory" "block"); for TYPE in "${array[@]}"; do [ "$1" = "$TYPE" ] && find /etc/ -type ${1:0:1}; done
Antwort2
Setzen Sie es in eine Funktion ein:
MyFind () {
COMMAND="find /etc -type "
case $1:
"directory") $COMMAND d
;;
esac
}
Jetzt können Sie es immer verwenden alsMyFind $TYPE
Zu Ihrem 1. Kommentar
Sie können auch nur die Case-Anweisung in eine Funktion einfügen
FType () {
case $1 in
"directory") echo d;;
"file") echo f;;
esac
}
COMMAND="find /etc -type "
$COMMAND $(FType $TYPE)
Antwort3
[[ "$1" == "directory" ]] && find /etc -type d
Antwort4
Nur eine weitere Idee, die die bereits große Auswahl an Optionen aus Sergs hervorragender Antwort ergänzt.
Möglicherweise können Sie hierfür einen verwenden alias
– möglicherweise mit einigen geringfügigen Änderungen gegenüber Ihrer aktuellen Vorgehensweise.
Ein alias
ist einfach ein Wort, das Sie einer längeren Zeichenfolge zuordnen möchten, die die Shell erweitert, wenn sie darauf stößt. Der wohl am häufigsten vorkommende Anwendungsfall ist die Anwendung von „defaults“ auf einen vorhandenen Befehl, wie folgt:
alias ls='ls -a --color=auto'
Es gibt jedoch keine Anforderung, dass Ihr Befehl alias
nach einem vorhandenen Befehl benannt sein muss - oder sogar kürzer als sein Zielmuster - Sie könnten also beispielsweisealias tgzcreator='tar -czvf'
alias
es teilen sich die Lebensdauer mit der Shell, in der sie definiert sind. 'Persistente' alias
es können für Sie in eingerichtet werden ~/.bash_aliases
, wassollenwird automatisch von den meisten gut geschriebenen Standardskripten .bashrc
und dergleichen bezogen.
Beachten Sie ein paar Hinweise:
- Anstatt sich in einem bestimmten Skript Sorgen darüber zu machen, ob ein zuvor definiertes Element
alias
Ihren Code beeinträchtigt, können Sie es mit einem Backslash voranstellen, um sicherzustellen,alias
dass ing übersprungen wird. Normalerweisealias
cp
mache ich das beispielsweisecp -i
, um ein versehentliches Überschreiben zu vermeiden, aber in einigen Skripten mache ich das eindeutigwollenum Dinge zu überschreiben. (Ich werde keinen schrecklichen Workaround verwenden, wie das Einrichten eines bekannten unedalias
-Benutzers!) Also werde ich in diesem Skript verwenden\cp src dst
alias
es werden möglicherweise nicht standardmäßig in Shell-Skripten verwendet, die ihre eigene, nicht interaktive Kopie der Shell starten. Sie können sicherstellen, dass sie erweitert werden, indem Sie die Optionexpand_aliases
in Ihrem Skript festlegen. Ich habe das hier:https://stackoverflow.com/questions/2197461/wie-man-einen-Alias-inside-a-bash-shell-script-einstellt-so-dass-er-von-der-ou-aus-sichtbar-ist
Ausgehend vom begrenzten Kontext, der in Ihrem Beitrag zur Verfügung steht, möchten Sie vielleicht etwas tunirgendwieso was:
shopt -s expand_aliases
alias get_etc_dir='find /etc -type d'
alias get_etc_fil='find /etc -type f'
Für Sie funktioniert dies möglicherweise nicht ohne Anpassungen, z. B. indem Sie Ihren parametrisierten Inode-Typ in ein Alias-Suffix ändern. Es ist nur eine weitere Option, wie Benutzer Codeteile im Allgemeinen verkürzen können. Wie dem auch sei, ich habe versucht, es auf der Grundlage dessen, was ich weiß, umfassend zu erklären, und ich hoffe, es ist irgendwo nützlich.
(Außerdem würde ich vorschlagen, dies auf Unix/Linux SE zu verschieben, vorausgesetzt, das ist ideal für Dinge, die nicht Ubuntu-spezifisch sind?)