このカスタム関数を実行するために必要なコードを最小限に抑えるにはどうすればよいですか?

このカスタム関数を実行するために必要なコードを最小限に抑えるにはどうすればよいですか?

スクリプトの 1 つを短くしたいのですが、アイデアはあるのですが、やり方がわかりません。次のようなコードがあります。

COMMAND="find /etc -type "
case $1:
 "directory") $COMMAND d
 ;;
esac

もちろん、これは短縮版です :) そして、$COMMAND をどこにでも書かずに、これを短縮できるようにしたいので、次のようになります。

$COMMAND <the case statement return value>

しかし、ケースの結果を格納するために変数を使用したくありません。

ありがとう :) 私が何をしたいのか理解していただけたら嬉しいです :D

編集 1: Serg が指摘したように、関数を作成して find パラメータを $1 として渡すことは可能です。関数を使わずに実行したい場合は、方法があるはずです :D Serg が解決しなかったわけではありませんが、ただ興味があるだけです :D

答え1

コードを簡素化する方法はいくつかあります。以下はコード量順に並べた解決策です。

  1. printfおよびパラメータ置換(エラーチェックなし)
  2. xargsおよびパラメータ置換(エラーチェックなし)
  3. findパラメータ置換のみ(エラーチェックなし)
  4. フォールスルーケース構造 (エラーチェックの問題を解決します)
  5. テストロジック、xargs、パラメータ置換
  6. Bash関数
  7. 配列、forループ、テスト、&&

  1. printfパラメータ置換ソリューション

    この関数のあまり知られていない特徴printfは、 を呼び出すとprintf "%c" someString初めその文字列の文字。したがって、パラメータ展開を伴う case ステートメントの使用を避けることができ、printf次のようになります。

    xieerqi:$ cat someScript.sh
    #!/bin/bash
    find /etc -type $(printf "%c" $1 )
    

    次を実行します:

    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
    

    ここでの制限は、 を呼び出すプロセスをフォークすることですがprintf、これは関数ソリューションによって回避されます。関数とケース構造はすべてネイティブの bash ツールです。

  2. xargsパラメータ置換

    bashのパラメータ置換を使用すると、変数から部分文字列を切り取ることができます(たとえば、${VAR:0:3}の最初の3文字を取得しますVAR)。この場合、型の最初の文字directoryまたはを使用します。次に、を使用してそれをパラメータとして渡すfileことができます。xargsfind

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

    マニュアルfindページには、-typeSolaris 上の flag には大文字で表される door ファイルと呼ばれるものがあると記載されていますDが、ここでは Linux を使用しているため、これは無視しても問題ない制限であると言えます。

    しかし、このコードには別の危険があります。ユーザーがパラメータflowerとして を入力した場合$1、文字列の最初の文字を取得するため、 を検索し続けます-type f。言い換えると、エラーチェックはありません

  3. findパラメータ拡張付き

    パラメータ拡張をさらに進めると、次のようになります。

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

    find基本的に、コマンドと変数の部分文字列を含むワンライナーです$1また、エラーチェックも行われない

  4. フォールスルーケース構造

    最後の 3 つの方法の大きな問題は、エラー チェックです。これらの方法は、ユーザーがダミーではないと信頼できる場合や、自分でコードを書いているだけの場合に適しています。Java では、コマンドswitchを省略するだけで、複数のケースで同じコマンドを実行するステートメントを記述できますbreak。ターミネータbashを使用しても、同じことができます;&man bash

    ;&の代わりに を使用すると;;、次のパターン セットに関連付けられたリストで実行が続行されます。

    必要なのは、「ディレクトリ」、「ファイル」、「ブロック」などの型をテストし、パラメータ置換を使用して最初の文字を切り取るだけです。

    #!/bin/bash
    case "$1" in
      "directory") ;&
      "file");&
      "block") find /etc -type  ${1:0:1} ;;
      *) exit 1 ;;
    esac
    

5.テストロジック、xargs、パラメータ置換

 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. 関数ソリューション

    findコマンドを関数内に配置し、位置パラメータを使用して呼び出すことができます。

    例えば:

    function findStuff
    {
     find /etc -type "$1" 
    }
    

    ここに小さなデモがあります。sudo多くのファイルでは/etc一般ユーザーには読み取り権限がないため、これを使用していることに注意してください。

    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. 配列、forループ、テスト、&&

    ここでの基本的な考え方は、ユーザーの入力をリストと照合し、一致した場合には何かを行うというものです。チェックする項目の配列を作成し、テスト条件を設定し (角括弧はtestcommand の別名です)、ループを実行して $1 変数をテストします。&&演算子を使用すると、左側のものが&&成功した場合にのみ command を実行できます。したがって、配列内にある文字列が見つかった場合は、find コマンドを実行します。${1:0:1} は、前の例で説明しました。これは、一致した型から最初の文字を切り取るパラメーター展開です。したがって、このソリューションにはエラー チェックがあり、大量のコードが 3 行 (#!行を含めると 4 行) にまとめられています。

    #!/bin/bash   
    array=("file" "directory" "block");
    for TYPE in "${array[@]}"; do 
       [ "$1" = "$TYPE"  ] && find /etc/ -type ${1:0:1}; 
    done  
    

答え2

これを関数に組み込みます:

MyFind () {
  COMMAND="find /etc -type "
  case $1:
   "directory") $COMMAND d
   ;;
  esac
}

いつでも使えるようになりましたMyFind $TYPE


最初のコメントに関して

関数にcase文だけを入れることもできる

FType () {
  case $1 in
    "directory") echo d;;
    "file") echo f;;
  esac
}

COMMAND="find /etc -type "
$COMMAND $(FType $TYPE) 

答え3

[[ "$1" == "directory" ]] && find /etc -type d

答え4

Serg の優れた回答から得られる、すでに幅広いオプション セットに追加するもう 1 つのアイデアです。

これには を使用できる可能性がありますalias- おそらく、現在のやり方に若干の変更を加える必要があります。

alias、より大きな文字列にマップするために選択した単語であり、シェルはそれを見つけるたびにそれを展開します。おそらく最も一般的な使用例は、次のように既存のコマンドに「デフォルト」を適用することです。

alias ls='ls -a --color=auto'

ただし、既存のコマンドにちなんで名前を付ける必要はなく、宛先パターンよりも短くする必要もありaliasません。たとえば、alias tgzcreator='tar -czvf'

aliasesは、それが定義されているシェルと存続期間を共有します。 '永続的' aliasesは、 で設定できます~/.bash_aliasesすべきよく書かれたデフォルトの.bashrcスクリプトなどによって自動的にソース化されます。

いくつかのヒントに注意してください:

  • 特定のスクリプトで、以前に定義された がコードに干渉するかどうかを心配するのではなくalias、 をスキップするためにバックスラッシュを前に付けることができますalias。たとえば、通常は誤って上書きするのを避けるためにalias cpを使用しますcp -iが、一部のスクリプトでは、欲しい上書きする。(既知の未編集ユーザーを設定するようなひどい回避策は使いませんalias!)そこで、このスクリプトでは、\cp src dst
  • aliasシェル スクリプト内では、デフォルトでは es がソース化されない場合があります。シェル スクリプトは、独自の非対話型シェル コピーを起動します。expand_aliasesスクリプトでオプションを設定することで、es が展開されるようにすることができます。これは次の場所から取得しました:https://stackoverflow.com/questions/2197461/how-to-set-an-alias-inside-a-bash-shell-script-so-that-is-it-visible-from-the-ou

したがって、投稿で利用可能な限られたコンテキストに基づいて、何かをしたいかもしれませんちょっとこのような:

shopt -s expand_aliases
alias get_etc_dir='find /etc -type d'
alias get_etc_fil='find /etc -type f'

あなたにとって、これは微調整なしでは機能しないかもしれません。たとえば、パラメータ化された inode タイプをエイリアスごとのサフィックスに変更します。これは、ユーザーが一般的にコードの一部を短縮する方法のもう 1 つのオプションにすぎません。いずれにせよ、私は自分の知識に基づいて包括的に説明しようとしました。どこかで役立つことを願っています。

(また、Ubuntu に特化していないものについてはこれが理想的だと仮定すると、これを Unix/Linux SE に移行することをお勧めします。)

関連情報