Як я можу використовувати команду bash, якщо разом перевірити та знайти команди?


25

У мене є каталог з журналами аварійного завершення, і я хотів би використовувати умовний оператор у скрипті bash на основі команди find.

Файли журналу зберігаються в такому форматі:

/var/log/crashes/app-2012-08-28.log
/var/log/crashes/otherapp-2012-08-28.log

Я хочу, щоб оператор if повертав істину лише в тому випадку, якщо для конкретного додатка є журнал аварій, який було змінено протягом останніх 5 хвилин. findКоманда , яку я хотів би використовувати це:

find /var/log/crashes -name app-\*\.log -mmin -5

Я не впевнений, як правильно це включити до ifзаяви. Я думаю, що це може спрацювати:

if [ test `find /var/log/crashes -name app-\*\.log -mmin -5` ] then
 service myapp restart
fi

Є кілька областей, де мені незрозуміло:

  • Я переглянув прапори if, але не впевнений, який із них, якщо такі будуть, я повинен використовувати.
  • Чи потрібна мені testдиректива чи мені просто обробляти результати результатів команди find або, можливо, використовувати find... | wc -lдля отримання підрахунку рядків?
  • Не потрібно на 100% відповісти на це запитання, але testце для тестування повертаючих кодів, що команди повертаються? І вони начебто невидимі - поза stdout/ stderr? Я прочитав manсторінку, але мені все ще незрозуміло, коли її використовувати testта як її налагодити.

Справжня відповідь на загальний випадок - використовувати find ... -exec. Також дивіться приклади команд у розділі Чому циклічно переходить на вихідну погану практику?
Wildcard

@Wildcard - на жаль, це не вирішує загальний випадок: він не працює, якщо є більше одного матчу, і дія потрібно виконати лише один раз, і вона не працює, якщо вам потрібна дія, яку слід виконати, коли є немає відповідностей. Перший може бути вирішений за допомогою ... -exec command ';' -quit, але я не вірю, що для останнього існує рішення, окрім розбору результату. Крім того, в будь-якому випадку головна проблема з розбором результату find(тобто неможливість відрізнити роздільники від символів у іменах) не застосовується, оскільки не потрібно знаходити роздільники в цих випадках.
Жуль

Відповіді:


27

[і testє синонімами (крім [необхідних ]), тому ви не хочете використовувати [ test:

[ -x /bin/cat ] && echo 'cat is executable'
test -x /bin/cat && echo 'cat is executable'

testповертає нульовий статус виходу, якщо умова є істинною, інакше не є нульовою. Це може бути фактично замінено будь-якою програмою, щоб перевірити її статус виходу, де 0 вказує на успіх, а не нуль вказує на збій:

# echoes "command succeeded" because echo rarely fails
if /bin/echo hi; then echo 'command succeeded'; else echo 'command failed'; fi

# echoes "command failed" because rmdir requires an argument
if /bin/rmdir; then echo 'command succeeded'; else echo 'command failed'; fi

Однак усі вищенаведені приклади лише перевіряють стан виходу програми та ігнорують вихід програми.

Тому що findвам потрібно буде перевірити, чи був створений якийсь результат. -nтести на порожню рядок:

if [[ -n $(find /var/log/crashes -name "app-*.log" -mmin -5) ]]
then
    service myapp restart
fi

Повний список тестових аргументів можна шляхом виклику help testв bashкомандному рядку.

Якщо ви використовуєте bash(а ні sh), ви можете скористатися [[ condition ]], що поводиться більш передбачувано, коли у вашому стані є пробіли чи інші особливі випадки. Інакше це, як правило, те саме, що і використання [ condition ]. Я використовував [[ condition ]]у цьому прикладі, як це роблю, коли це можливо.

Я також змінився `command`на $(command), який також зазвичай поводиться аналогічно, але приємніше з вкладеними командами.


echoне може: спробуйте echo 'oops' > /dev/full.
дероберт

Ця відповідь лежить у корені проблеми, але витончено уникає згадувати саме те, що це таке.
Багамат

9

findвийде успішно, якщо не було жодних помилок, тому ви не можете розраховувати на його статус виходу, щоб знати, чи знайшов він якийсь файл. Але, як ви сказали, ви можете порахувати, скільки знайдених файлів і протестувати це число.

Це було б щось подібне:

if [ $(find /var/log/crashes -name 'app-*.log' -mmin -5 | wc -l) -gt 0 ]; then
    ...
fi

test(ака [) не перевіряє коди помилок команд, у нього є спеціальний синтаксис для тестування, а потім виходить з кодом помилки 0, якщо тест був успішним, або 1 в іншому випадку. Це ifтой, хто перевіряє код помилки команди, яку ви йому передаєте , та виконує його тіло на основі цього.

Дивіться man test(або help test, якщо ви використовуєте bash) та help if(ditto).

У цьому випадку wc -lвиведе число. Ми використовуємо testопцію -gtдля тестування, чи число це більше 0. Якщо він є, test(або [) повернеться з кодом виходу 0. ifбуде інтерпретувати цей вихідний код як успіх, і він запустить код всередині свого тіла.


1

Це було б

if [ -n "$(find /var/log/crashes -name app-\*\.log -mmin -5)" ]; then

або

if test -n "$(find /var/log/crashes -name app-\*\.log -mmin -5)"; then

Команди testі [ … ]точно синонімічні. Єдина відмінність - їхня назва та факт, що [потребує закриття ]як останнього аргументу. Як завжди, використовуйте подвійні лапки навколо заміни команди, інакше вихід findкоманди буде розбитий на слова, і тут ви отримаєте синтаксичну помилку, якщо є більше одного відповідного файлу (а коли немає аргументів, [ -n ]правда , тоді як ви хочете, [ -n "" ]що неправда).

В ksh, bash і zsh, але не в золі, ви також можете використовувати [[ … ]] різні правила розбору: [це звичайна команда, тоді [[ … ]]як інша конструкція розбору. Вам не потрібні подвійні лапки всередині [[ … ]](хоча вони не зашкодять). Вам все ще потрібна команда ;після.

if [[ -n $(find /var/log/crashes -name app-\*\.log -mmin -5) ]]; then

Це потенційно може бути неефективним: якщо в файлах багато /var/log/crashes, пошук знайде їх усі. Вам слід зробити пошук зупинки, як тільки він знайде збіг, або незабаром після цього. При пошуку GNU (невбудований Linux, Cygwin) використовуйте -quitосновний.

if [ -n "$(find /var/log/crashes -name app-\*\.log -mmin -5 -print -quit)" ]; then

З іншими системами, трубами findINTO , headпо крайней мере , вийти незабаром після першого матчу (знахідка буде померти від розбитої труби).

if [ -n "$(find /var/log/crashes -name app-\*\.log -mmin -5 -print | head -n 1)" ]; then

(Ви можете використовувати, head -c 1якщо ваша headкоманда підтримує це.)


Як варіант, використовуйте zsh.

crash_files=(/var/log/crashes/**/app-*.log(mm-5[1]))
if (($#crash_files)); then


0
find /var/log/crashes -name app-\*\.log -mmin -5 -exec service myapp restart ';' -quit

тут є відповідним рішенням.

-exec service myapp restart ';' причини find викликати команду, яку потрібно запустити безпосередньо, а не потрібна оболонці, щоб щось інтерпретувати.

-quitвикликає findвихід після обробки команди, тим самим запобігаючи виконанню команди знову, якщо трапляється кілька файлів, що відповідають критеріям.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.