Наша совместная команда Banwar.org

Связаться с нами

  • (097) ?601-88-87
    (067) ?493-44-27
    (096) ?830-00-01

Статьи

Перенаправлення в bash за допомогою exec

Якщо ви часто працюєте в командному рядку, ви повинні бути знайомі з перенаправленням введення / виведення. Але, можливо, ви не знайомі з перенаправленням введення / виведення всередині bash-скрипта. Я не маю на увазі перенаправлення, що використовується при виклику інших програм з скрипта, я говорю про перенаправлення вводу / виводу вашого скрипта в цілому, починаючи з моменту його запуску.

Я не маю на увазі перенаправлення, що використовується при виклику інших програм з скрипта, я говорю про перенаправлення вводу / виводу вашого скрипта в цілому, починаючи з моменту його запуску

Давайте, наприклад, припустимо, що вам знадобилося забезпечити ваш скрипт опцією «--log», за допомогою якої користувач міг би перенаправляти весь висновок роботи вашого скрипта в потрібний лог-файл. Звичайно, користувач міг би просто перенаправити висновок скрипта засобами bash, але давайте уявимо, що з якихось причин таке рішення не годиться. Давайте реалізуємо це:

#! / Bin / bash echo hello # Розбираємо опції командного рядка. # Виконуємо наступний код, якщо знайшли опцію --log if test -t 1; then # Stdout прив'язаний до терміналу exec> log else # Stdout не прив'язаний до терміналу, # протокол вести не вийде false fi echo goodbye echo error> & 2

У першому затвердження if за допомогою test виконується перевірка, чи підключено файловий дескриптор з номером один (потік стандартного виведення) до терміналу. Якщо це так, то exec «перевідкривається» його для запису в файл log. Виклик exec в контексті поточної оболонки без передачі їй команди, а тільки з зазначенням перенаправлення виведення, призводить до того, що ви можете відкривати / закривати файли, дублюючи їх дескриптори. Якщо ж файловий дескриптор з номером 1 не підключено до терміналу, то ми просто не робимо нічого.

Якщо ви запустите вищенаведений скрипт, то ви побачите, що перша і остання echo виконають свій висновок в термінал. Перша echo спрацює тому, що вона з'являється до включення перенаправлення, а друга - оскільки в сценарії її висновок перенаправлений в стандартний потік помилок (файловий дескриптор номер 2). І як же перенаправити стандартний потік помилок в той же файл? Всього Чи одне невелика зміна у виклику exec:

#! / Bin / bash echo hello if test -t 1; then # Stdout прив'язаний до терміналу exec> log 2> & 1 else # Stdout не прив'язаний до терміналу, # протокол вести не вийде false fi echo goodbye echo error> & 2

В наведеному вище прикладі exec перенаправляє потік помилок туди ж, куди спрямований потік виводу (це, власне, і називається дублюванням файлових дескрипторів). Майте на увазі, що порядок тут дуже важливий: якщо ви його зміните і переоткроете спершу потік помилок (т. Е. Exec 2> & 1> log), то весь висновок все одно залишиться спрямованим на термінал, оскільки він буде направлений туди ж, куди і потік виведення, а той, у свою чергу за замовчуванням спрямований в термінал.

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

Візьмемо, наприклад, команду:

bash test.sh | grep good

Те, що нам потрібно в кінцевому підсумку, це еквівалент наступної команди:

bash test.sh | tee log | grep good

Ймовірно, вашої першою думкою буде викликати exec якось так:

exec | tee log & # працювати не буде

намагаючись таким чином за допомогою exec перенаправити висновок в tee, запущеної в фоновому режимі. Але це не буде працювати (хоча bash нічого і не скаже). Така конструкція всього лише перенаправляє висновок exec в tee, а оскільки exec не виводить нічого, то tee створить порожній файл і на цьому закінчить.

Є ще одна думка: запустити tee у фоновому режимі, направивши в неї введення з одного файлового дескриптора, а висновок перенапровіть в інший. І ви можете це зробити, але проблема в тому, що не існує способу створити новий процес, стандартне введення якого був би підключений до каналу. Якби ми могли це зробити, то отримати висновок tee було б просто, оскільки за замовчуванням він спрямований туди ж, куди і висновок сценарію цілком. Таким чином, ми могли б просто закрити потік виведення сценарію і підключити його до нашого каналу, якби була можливість цей канал створити.

Виходить, ми в глухому куті? Ні. Рішення описано в останніх двох реченнях попереднього абзацу. Нам потрібно лише створити канал, вірно? Відмінно, давайте використовувати іменовані канали.

#! / Bin / bash echo hello if test -t 1; then # Stdout is a terminal. exec> log else # Stdout is not a terminal. npipe = / tmp / $$. tmp trap "rm -f $ npipe" EXIT mknod $ npipe p tee & - exec 1> $ npipe fi echo goodbye

Тут, якщо потік стандартного висновку не підключений до терміналу, ми створюємо іменований канал (канал, який розташовується у вигляді файлу в файлової системі) за допомогою mknod, і за допомогою trap видаляємо його після завершення роботи сценарію. Потім ми запускаємо tee, пов'язуючи його потік введення зі створеним каналом і вказуємо записувати відео у файл log. Пам'ятайте, що tee крім запису в файл всього отриманого з потоку введення, також виводить все в потік стандартного виведення. Також, згадайте про те, що потік виведення tee направлений туди ж, куди і весь висновок сценарію, що викликає tee. Таким чином, весь висновок tee потраплятиме туди, куди в даний момент спрямований потік виведення нашого сценарію, тобто, в перенаправлений користувачем потік виведення або канал конвеєра, задані в момент виклику сценарію з командного рядка. Так що тепер ми отримали стандартний висновок tee там, де нам це потрібно: в перенаправлення або канал конвеєра, певний користувачем.

Тепер залишається передати на вхід tee потрібні дані. Оскільки tee тепер зчитує дані з іменованого каналу, все що нам необхідно, це перенаправити стандартний висновок в іменований канал. Ми закриваємо поточний потік виводу (exec 1> & -) і відкриваємо його в іменований канал (exec 1> $ npipe). Зверніть увагу, що закриття поточного потоку виведення нічого не порушує, оскільки tee також здійснює висновок в перенаправлений користувачем потік або канал.

Тепер, коли ви виконаєте команду і направите її висновок в канал до grep, ви отримаєте і стандартний висновок і запис в лог-файлі.

Подібних прийомів маса, відкрийте man-сторінку bash!

PS У Bash 4 те ж саме можна зробити за допомогою конструкції coproc, але про це іншим разом.

Mitch Frazier is an Associate Editor for Linux Journal and the Web Editor for linuxjournal.com.

По мотивам linuxjournal.com .

І як же перенаправити стандартний потік помилок в той же файл?
Виходить, ми в глухому куті?
Нам потрібно лише створити канал, вірно?

Новости

Banwar.org
Наша совместная команда Banwar.org. Сайт казино "Пари Матч" теперь доступен для всех желающих, жаждущих волнения и азартных приключений.