Придушіть слід виконання bash (set -x) ззовні скрипту


17

Я спробував знайти відповідь на це питання, але поки не пощастило:

У мене є сценарій, який запускає деякі інші сценарії, і багато хто з цих інших скриптів мають "set -x" в них, що змушує їх друкувати кожну команду, яку вони виконують. Я хотів би позбутися цього, але зберегти інформацію, якщо будь-який із сценаріїв надішле повідомлення про помилку на stderr.

Тому я не можу просто писати ./script 2>/dev/null

Також у мене немає привілеїв редагувати ці інші сценарії, тому я не можу вручну змінити встановлений параметр.

Я думав над тим, щоб записати все від stderr до окремого файлу та відфільтрувати команди відстеження, але, можливо, є простіший спосіб?


1
./script 2>some_file
Satō Katsura

Відповіді:


25

З bash4.1 і вище, ви можете зробити

BASH_XTRACEFD=7 ./script.bash 7> /dev/null

(також працює, коли bashвикликається як sh).

В основному, ми хочемо bashвивести xtraceвихід на дескриптор файлу 7 замість 2 за замовчуванням і перенаправити цей дескриптор файлу на /dev/null. Номер fd довільний. Використовуйте fd вище 2, який інакше не використовується у вашому сценарії. Якщо оболонка, в яку ви вводите цю команду, є bashабо yashви навіть можете використовувати число вище 9 (хоча ви можете зіткнутися з проблемами, якщо дескриптор файлу використовується оболонкою всередині).

Якщо оболонка, з якої ви викликаєте цей bashскрипт zsh, ви також можете зробити:

(export BASH_XTRACEFD; ./script.bash {BASH_XTRACEFD}> /dev/null)

для змінної автоматично призначається перший вільний fd вище 9.

Для старих версій bashіншим варіантом, якщо xtraceввімкнено set -xфункцію (на відміну від #! /bin/bash -xабо set -o xtrace), буде переосмислити setяк експортовану функцію, яка нічого не робить при передачі -x(хоча це порушить сценарій, якщо він (або будь-який інший bashскрипт, який він викликає) використовується setдля встановлення позиційних параметрів).

Подобається:

set()
  case $1 in
    (-x) return 0;;
    (-[!-]|"") builtin set "$@";;
    (*) echo >&2 That was a bad idea, try something else; builtin set "$@";;
  esac

export -f set
./script.bash

Інший варіант - додати пастку DEBUG у $BASH_ENVфайл, який виконується set +xперед кожною командою.

echo 'trap "{ set +x; } 2>/dev/null" DEBUG' > ~/.no-xtrace
BASH_ENV=~/.no-xtrace ./script.bash

Це не спрацює, якщо set -xце зроблено в підколонці.

Як сказав @ilkkachu, якщо у вас є дозвіл на запис у будь-яку папку файлової системи, ви повинні принаймні мати можливість зробити копію сценарію та відредагувати його.

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

 bash <(sed 's/set -x/set +x/g' ./script.bash)

Це (і підхід до копіювання) може не працювати належним чином, якщо сценарій робить щось фантазійне $0або спеціальні змінні на зразок $BASH_SOURCE(наприклад, пошук файлів, що відносяться до місця розташування самого сценарію), тому вам може знадобитися зробити ще одне редагування, наприклад замінити $0на шлях сценарію ...


Ваша перша відповідь - саме те, що мені було потрібно, чисте та елегантне. Чи можете ви пояснити трохи, як це працює? Чому число 7? Для чого можуть використовуватися інші числа? Спасибі.
людина

@human, див. редагування
Stéphane Chazelas

1
{BASH_XTRACEFD}>Трюк працює в bash4.1 або більш пізньої версії , а також.
чепнер

@chepner, так, функція була додана до zsh, ksh93 та bash одночасно за пропозицією розробника zsh. Але, тут він не працює для ksh93або bashв тому, що змінна не передається в середовищі команди (порівняти <shell> -c 'export fd; printenv fd {fd}> /dev/null'в zsh, bashі ksh93). Ви можете змусити його працювати в ksh93/ bashвиконавши це в два етапи або, можливо, скориставшись eval, але для bashцього, якщо ввімкнено параметр xtrace, це матиме побічні ефекти.
Стефан Шазелас

5

Оскільки вони сценарії, ви можете зробити їх копії та відредагувати їх.

Окрім цього, фільтрування результату здавалося б простим, ви можете явно встановити PS4щось більш незвичне, ніж єдиний плюс, щоб полегшити фільтрацію:

PS4="%%%%" bash script.sh 2>&1 | grep -ve '^%%%%'

(звичайно, це зруйнує stdout та stdin, але трубопроводи просто stderr у Bash трохи волохаті, тому я ігнорую це)


1
Трубопровід просто STDERR легко: PS4="%%%%" bash script.sh 2> >(grep -ve '^%%%%').
Патрік

4
@Patrick, виконуючи це в bashпроблемах, оскільки grepце запускається асинхронно (bash його не чекає, тому він може (і часто це робить) виводити речі після запуску наступної команди в скрипті).
Стефан Шазелас
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.