이 사용자 정의 기능을 실행하는 데 필요한 코드를 최소화하려면 어떻게 해야 합니까?

이 사용자 정의 기능을 실행하는 데 필요한 코드를 최소화하려면 어떻게 해야 합니까?

내 스크립트 중 하나를 단축하고 싶은데 아이디어는 있지만 어떻게 해야 할지 모르겠습니다. 다음과 같은 코드가 있습니다.

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

물론 이것은 짧은 버전입니다. :) 이제 $COMMAND를 어디서나 쓸 필요 없이 단축할 수 있기를 원하므로 다음과 같은 것을 갖고 싶습니다.

$COMMAND <the case statement return value>

하지만 케이스 결과를 저장하기 위해 변수를 사용하고 싶지 않습니다.

감사합니다 :) 제가 원하는 것을 이해하셨기를 바랍니다. :D

편집 1: 함수를 생성하고 Serg가 가리키는 대로 찾기 매개변수를 $1로 전달할 수 있습니다. 이제 기능 없이 그냥 하고 싶었다면 방법이 있을 거라 확신합니다 :D Serg가 문제를 해결하지 못한 것처럼, 그냥 궁금해서요 :D

답변1

코드를 단순화하는 방법에는 여러 가지가 있습니다. 다음은 코드 양에 따라 정렬된 솔루션입니다.

  1. printf및 매개변수 대체(오류 검사 없음)
  2. xargs및 매개변수 대체(오류 검사 없음)
  3. find및 매개변수 대체만(오류 검사 없음)
  4. Fall-through 케이스 구조 (오류 확인 문제 해결)
  5. 테스트 논리, xargs 및 매개변수 대체
  6. 배쉬 기능
  7. 배열, for 루프, 테스트 및 &&

  1. printf및 매개변수 대체 솔루션

    함수 의 잘 알려지지 않은 품질은 printf호출하면 printf "%c" someString다음과 같은 내용만 인쇄한다는 것입니다.첫 번째해당 문자열의 문자입니다. 따라서 printf다음과 같이 매개변수 확장이 포함된 Case 문을 사용하는 것을 피할 수 있습니다 .

    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. 그런 다음 xargs이를 매개변수로 전달하는 데 사용할 수 있습니다 .find

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

    매뉴얼 find페이지에는 Solaris의 플래그에 대해 -type대문자로 표시되는 도어 파일이라는 것이 있다고 언급되어 있지만 DLinux를 사용하고 있으므로 이는 무시해도 되는 제한 사항이라고 말할 수 있습니다.

    그러나 이 코드에는 또 다른 위험이 있습니다. 사용자가 매개변수 flower로 입력하면 문자열의 첫 번째 문자를 가져오기 때문에 $1계속 검색하게 됩니다 -type f. 즉,오류 검사가 없습니다.

  3. find매개변수 확장 포함

    매개변수 확장을 더욱 발전시키면 다음과 같이 할 수 있습니다.

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

    find기본적으로 명령과 변수의 하위 문자열이 포함된 한 줄짜리입니다 $1.또한 오류 검사도 하지 않습니다..

  4. 낙하 케이스 구조

    마지막 세 가지 방법의 가장 큰 문제는 오류 검사입니다. 사용자가 가짜가 아니라고 믿을 때나 스스로 코드를 작성할 때 유용합니다. 이제 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 루프, 테스트 및 &&

    여기서의 기본 아이디어는 사용자의 입력을 목록과 일치시키고, 일치하면 뭔가를 하는 것입니다. 확인할 항목 배열을 만들고, 테스트 조건(대괄호는 test명령의 별칭임)을 갖고, 루프를 실행하여 $1 변수를 테스트합니다. &&연산자는 왼쪽에 있는 것이 &&성공한 경우에만 명령 실행을 허용합니다. 따라서 배열에 있는 문자열을 찾으면 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의 탁월한 답변에서 이미 광범위한 옵션 세트에 추가하는 또 다른 아이디어입니다.

이를 위해 an을 사용할 수 있습니다 alias. 아마도 현재 작업을 수행하는 방식을 약간 변경하면 됩니다.

An은 alias더 큰 문자열에 매핑하기 위해 선택한 단어일 뿐이며, 이를 만날 때마다 쉘이 확장됩니다. 아마도 가장 흔히 볼 수 있는 사용 사례는 다음과 같이 기존 명령에 '기본값'을 적용하는 것입니다.

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

alias그러나 기존 명령의 이름을 따서 이름을 지정해야 하거나 대상 패턴보다 더 짧게 명명해야 한다는 요구 사항은 없습니다 . 예를 들어 다음과 같이 할 수 있습니다.alias tgzcreator='tar -czvf'

aliases는 정의된 쉘과 수명을 공유합니다. '영구' alias는 에서 설정할 수 있습니다 ~/.bash_aliases.~해야 한다가장 잘 작성된 기본 .bashrc스크립트 등에 의해 자동으로 소스가 제공됩니다.

몇 가지 힌트를 참고하세요.

  • 이전에 정의한 내용이 코드를 방해할지 여부를 특정 스크립트에서 걱정하는 대신 alias백슬래시를 앞에 붙여 aliasing을 건너뛰도록 할 수 있습니다. 예를 들어 실수로 덮어쓰는 것을 방지하기 위해 일반적 alias cp으로 사용 cp -i하지만 일부 스크립트에서는 분명히원하다덮어쓰려고요. (알려진 uned 사용자를 설정하는 것과 같은 끔찍한 해결 방법은 사용하지 않을 것입니다 alias!) 따라서 해당 스크립트에서 다음을 사용하겠습니다.\cp src dst
  • aliases는 자체 비대화형 쉘 복사본을 실행하는 쉘 스크립트 내에서 기본적으로 소스가 제공되지 않을 수 있습니다. expand_aliases스크립트에서 옵션을 설정하여 확장되도록 할 수 있습니다 . 나는 이것을 다음에서 얻었습니다 :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 유형을 별칭별 접미사로 변경하는 등의 조정 없이는 작동하지 않을 수 있습니다. 이는 사용자가 일반적으로 코드의 일부를 단축할 수 있는 방법에 대한 또 다른 옵션일 뿐입니다. 어느 쪽이든 제가 알고 있는 내용을 바탕으로 종합적으로 설명하려고 노력했는데 어딘가에 도움이 되었으면 좋겠습니다.

(또한 우분투에만 국한되지 않는 것에 이상적이라고 가정하면 이것을 Unix/Linux SE로 옮기는 것이 좋습니다.)

관련 정보