Как можно минимизировать код, необходимый для запуска этой пользовательской функции?

Как можно минимизировать код, необходимый для запуска этой пользовательской функции?

Я хотел бы сократить один из моих скриптов, и у меня есть идея, но я не знаю, как это сделать. Есть кусок кода вроде этого:

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

Конечно, это короткая версия :) И теперь я хочу иметь возможность сократить ее, чтобы не писать эту $COMMAND везде, поэтому я хотел бы иметь что-то вроде этого:

$COMMAND <the case statement return value>

но я не хочу использовать переменную для хранения результата case.

Спасибо :) Надеюсь, вы поняли, чего я хочу :D

EDIT 1: Можно создать функцию и передать параметр find как $1, как указал Serg. Теперь, если бы я хотел сделать это просто без функции, я уверен, что есть способ :D Не то чтобы Serg не решил ее, мне просто интересно :D

решение1

Существует несколько возможных способов упрощения кода. Ниже приведены решения, упорядоченные по объему кода:

  1. printfи подстановка параметров (без проверки ошибок)
  2. xargsи подстановка параметров (без проверки ошибок)
  3. findи только подстановка параметров (без проверки ошибок)
  4. структура сквозного кейса (решает проблему проверки ошибок)
  5. Тестовая логика, xargs и подстановка параметров
  6. Функция Баша
  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, чего решение с использованием функции избегает — функция и структура case являются собственными инструментами bash.

  2. xargsи подстановка параметров

    Используя подстановку параметров bash, мы можем отрезать подстроку от переменной (например, ${VAR:0:3}дает первые 3 символа VAR); в этом случае нам нужен первый символ для типа directoryили file. Затем мы можем использовать xargs, чтобы передать это как параметр вfind

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

    На странице руководства findупоминается, что для -typeфлага в Solaris существует нечто, известное как файл door, который обозначается заглавной буквой D, но поскольку мы работаем в Linux, можно сказать, что это ограничение можно проигнорировать.

    Однако в этом коде есть еще одна опасность — если пользователь вводит 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

Еще одна идея в дополнение к уже широкому набору вариантов из превосходного ответа Сержа.

Вы можете использовать aliasдля этого — возможно, с небольшими изменениями в том, как вы сейчас что-то делаете.

An alias— это просто слово, которое вы выбираете для сопоставления с большей строкой, которую оболочка будет расширять всякий раз, когда с ней столкнется. Возможно, наиболее часто встречающийся вариант использования — это применение «значений по умолчанию» к существующей команде, например:

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

Однако нет требования, чтобы aliasимя вашей команды было таким же, как у существующей команды, или даже короче шаблона назначения, поэтому вы можете, например,alias tgzcreator='tar -czvf'

aliases разделяют время жизни с оболочкой, в которой они определены. 'Persistent' aliases для вас может быть настроен в ~/.bash_aliases, которыйдолженавтоматически запускаться большинством хорошо написанных .bashrcскриптов по умолчанию и т.п.

Обратите внимание на несколько подсказок:

  • Вместо того, чтобы беспокоиться о том, aliasпомешает ли ранее определенное в конкретном сценарии вашему коду, вы можете поставить перед ним обратную косую черту, чтобы гарантировать aliasпропуск ing. Например, я обычно alias cpделаю это, cp -iчтобы избежать случайной перезаписи, но в некоторых сценариях я четкохотетьдля перезаписи вещей. (Я не собираюсь использовать какой-то ужасный обходной путь, вроде настройки известного нередактированного aliasпользователя!) Итак, в этом скрипте я буду использовать\cp src dst
  • aliases не могут быть получены по умолчанию в скриптах оболочки, которые запускают собственную неинтерактивную копию оболочки. Вы можете гарантировать, что они развернуты, установив опцию expand_aliasesв вашем скрипте. Я получил это от:https://stackoverflow.com/questions/2197461/как-установить-псевдоним-внутри-скрипта-оболочки-bash-чтобы-он-видим-из-себя-ou

Итак, основываясь на ограниченном контексте, доступном в вашем посте, вы, возможно, захотите что-то сделатькак бытак:

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

Для вас это может не работать без настройки, например, изменения вашего параметризованного типа inode на суффикс per-alias. Это просто еще один вариант того, как пользователи могут сокращать фрагменты кода в целом. В любом случае, я попытался объяснить всесторонне, основываясь на том, что я знаю, и надеюсь, что это где-то пригодится.

(Кроме того, я бы предложил перенести это на Unix/Linux SE, предполагая, что это идеальный вариант для вещей, не специфичных для Ubuntu?)

Связанный контент