Wie kann ich den zum Ausführen dieser benutzerdefinierten Funktion erforderlichen Code minimieren?

Wie kann ich den zum Ausführen dieser benutzerdefinierten Funktion erforderlichen Code minimieren?

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:

  1. printfund Parametersubstitution (keine Fehlerprüfung)
  2. xargsund Parametersubstitution (keine Fehlerprüfung)
  3. findund nur Parameterersetzung (keine Fehlerprüfung)
  4. Fall-Through-Case-Struktur (löst das Problem der Fehlerprüfung)
  5. Testlogik, xargs und Parametersubstitution
  6. Bash-Funktion
  7. Array, For-Schleife, Test und &&

  1. printfund Parametersubstitutionslösung

    Eine wenig bekannte Eigenschaft der printfFunktion ist, dass beim Aufruf printf "%c" someStringnur dieErsteZeichen dieser Zeichenfolge. So können wir die Verwendung von Case-Anweisungen mit Parametererweiterung vermeiden und printfwie 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.

  2. xargsund Parametersubstitution

    Mithilfe der Parametersubstitution von bash können wir einen Teilstring aus einer Variablen abschneiden ( ${VAR:0:3}gibt beispielsweise die ersten 3 Zeichen von VAR); in diesem Fall möchten wir das erste Zeichen für einen Typ directoryoder file. Dann können wir xargsdies als Parameter an übergebenfind

    echo ${1:0:1} | xargs -I {} find /etc -type  {} 
    

    Die findManpage erwähnt, dass -typees für Flag unter Solaris etwas gibt, das als Door-Datei bekannt ist und durch den Großbuchstaben dargestellt wird D. 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 flowerals $1Parameter 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.

  3. findmit Parametererweiterung

    Wenn wir die Parametererweiterung noch weiter vorantreiben, können wir Folgendes tun:

     find /etc -type ${1:0:1}
    

    Im Grunde ein Einzeiler mit findBefehl und einer Teilzeichenfolge der $1Variablen.Auch keine Fehlerprüfung.

  4. 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 switchAnweisung zu schreiben, die denselben Befehl in mehreren Fällen ausführt, wenn Sie den breakBefehl einfach weglassen. bashDies 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.
  1. Funktionslösung

    findBefehl 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, sudoweil für viele Dateien /etcnormale Benutzer keine Leseberechtigung haben

    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
    
    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
    
  2. 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 testBefehle) 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 aliasist 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 aliasnach einem vorhandenen Befehl benannt sein muss - oder sogar kürzer als sein Zielmuster - Sie könnten also beispielsweisealias tgzcreator='tar -czvf'

aliases teilen sich die Lebensdauer mit der Shell, in der sie definiert sind. 'Persistente' aliases können für Sie in eingerichtet werden ~/.bash_aliases, wassollenwird automatisch von den meisten gut geschriebenen Standardskripten .bashrcund dergleichen bezogen.

Beachten Sie ein paar Hinweise:

  • Anstatt sich in einem bestimmten Skript Sorgen darüber zu machen, ob ein zuvor definiertes Element aliasIhren Code beeinträchtigt, können Sie es mit einem Backslash voranstellen, um sicherzustellen, aliasdass ing übersprungen wird. Normalerweise alias cpmache ich das beispielsweise cp -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 uned alias-Benutzers!) Also werde ich in diesem Skript verwenden\cp src dst
  • aliases 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 Option expand_aliasesin 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?)

verwandte Informationen