Я хотел бы сократить один из моих скриптов, и у меня есть идея, но я не знаю, как это сделать. Есть кусок кода вроде этого:
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
Существует несколько возможных способов упрощения кода. Ниже приведены решения, упорядоченные по объему кода:
printf
и подстановка параметров (без проверки ошибок)xargs
и подстановка параметров (без проверки ошибок)find
и только подстановка параметров (без проверки ошибок)- структура сквозного кейса (решает проблему проверки ошибок)
- Тестовая логика, xargs и подстановка параметров
- Функция Баша
- Массив, цикл for, тест и &&
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.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
, поскольку мы берем первый символ любой строки… Другими словами,нет проверки ошибок.find
с расширением параметровПродолжая расширение параметров, мы можем сделать следующее:
find /etc -type ${1:0:1}
По сути, это однострочный код с
find
командой и подстрокой переменной$1
.Также нет проверки ошибок.Проваливаемая конструкция корпуса
Большая проблема с последними тремя методами — это проверка ошибок. Они хороши, когда вы уверены, что пользователь не болван, или просто пишет код для себя. Теперь в 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.
Функциональное решение
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
Массив, цикл 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'
alias
es разделяют время жизни с оболочкой, в которой они определены. 'Persistent' alias
es для вас может быть настроен в ~/.bash_aliases
, которыйдолженавтоматически запускаться большинством хорошо написанных .bashrc
скриптов по умолчанию и т.п.
Обратите внимание на несколько подсказок:
- Вместо того, чтобы беспокоиться о том,
alias
помешает ли ранее определенное в конкретном сценарии вашему коду, вы можете поставить перед ним обратную косую черту, чтобы гарантироватьalias
пропуск ing. Например, я обычноalias
cp
делаю это,cp -i
чтобы избежать случайной перезаписи, но в некоторых сценариях я четкохотетьдля перезаписи вещей. (Я не собираюсь использовать какой-то ужасный обходной путь, вроде настройки известного нередактированногоalias
пользователя!) Итак, в этом скрипте я буду использовать\cp src dst
alias
es не могут быть получены по умолчанию в скриптах оболочки, которые запускают собственную неинтерактивную копию оболочки. Вы можете гарантировать, что они развернуты, установив опцию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?)