Як передати 2> / dev / null як змінну?


13

У мене є цей код, який працює:

# Hide irrelevant errors so chrome doesn't email us in cron
if [[ $fCron == true ]] ; then
    google-chrome --headless --disable-gpu --dump-dom \
        "$RobWebAddress" > "$DownloadName" 2>/dev/null
else
    # Get silly error messages when running from terminal
    google-chrome --headless --disable-gpu --dump-dom \
        "$RobWebAddress" > "$DownloadName"
fi

Якщо я спробую скоротити це так:

# Hide irrelevant errors so chrome doesn't email us in cron
local HideErrors
[[ $fCron == true ]] && HideErrors="2>/dev/null"

google-chrome --headless --disable-gpu --dump-dom \
    "$RobWebAddress" > "$DownloadName" "$HideErrors"

Я отримую повідомлення про помилки:

[0826/043058.634775:ERROR:headless_shell.cc(597)] Open multiple tabs is only supported when remote debugging is enabled.
[0826/043058.672587:ERROR:headless_shell.cc(597)] Open multiple tabs is only supported when remote debugging is enabled.
[0826/043058.711640:ERROR:headless_shell.cc(597)] Open multiple tabs is only supported when remote debugging is enabled.
(... SNIP ...)

Чому жорстко кодований аргумент працює, але не є аргументом як змінною?


Редагувати 2:

В даний час я знайшов успіх у альтернативній пропозиції другої відповіді:

# Redirect errors when cron is used to /dev/null to reduce emails
ErrorPipe=/dev/stderr
[[ $fCron == true ]] && ErrorPipe=/dev/null

google-chrome --headless --disable-gpu --dump-dom \
                "$RobWebAddress" > "$DownloadName" 2>"$ErrorPipe"

Редагувати 1:

Виходячи з першої відповіді, я повинен зазначити, що заголовок програми вже містить:

[[ $fCron != true ]] &&
    exec 2> >(grep -v 'GtkDialog mapped without a transient parent' >&2)

Ви можете спробувати [[ $fCron == true ]] && exec 2>/dev/nullзамість цього
steeldriver

.. грубо кажучи, це тому, що оболонка встановлює перенаправлення перед розширенням змінних. Дивіться, наприклад, bash: Використовуйте змінну для зберігання перенаправлення stderr | stdout
steeldriver

Відповіді:


19

Причина, через яку ви не можете спричинити перенаправлення шляхом розширення, "$HideErrors"полягає в тому, що такі символи >не обробляються спеціально після створення розширення параметрів . Це насправді дуже добре, тому що такі символи з’являються в тексті, які ви, можливо, захочете розширити і використовувати буквально.

Це стосується того, ви цитуєте чи ні $HideErrors. Результат розширення параметрів підлягає розщепленню і глобалізації, коли розширення не цитується, але це все.


Щодо того, що з цим робити, існує чимало способів досягти умовного перенаправлення. Для дуже простої команди може бути розумно записати всю команду двічі, один раз у кожну гілку конструкції caseабо if- else. Однак незабаром це стає обтяжливим, і команда, яку ви показали, безумовно, є випадком, коли це не було б ідеально.

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

Збережіть команду замість перенаправлення. Замість того, щоб намагатися зберегти перенаправлення у змінній та застосувати розширення параметра, збережіть команду у функції оболонки . Потім напишіть a caseабо if- else, в якому функція викликається перенаправленням на одній гілці, а без неї - на іншій.

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

З кодом:

launch() {
    google-chrome --headless --disable-gpu --dump-dom \
        "$RobWebAddress" > "$DownloadName"
}

case $fCron in
true)  launch 2>/dev/null;;
*)     launch;; # Get silly error messages when running from terminal
esac

Ви можете застосувати будь-який інтервал, який вам подобається, або if- elseзамість цього, якщо хочете. Зауважте, що launchавтоматично використовуються абоненти RobWebAddressта DownloadNameзмінні, навіть якщо вони є локальними змінними, оскільки Bash динамічно оцінюється , на відміну від більшості мов програмування, які мають лексичний діапазон.

Запустіть команду в нижній частині і умовно застосуйте перенаправлення на exec. Це те, що прокоментував Steeldriver , але всередині ( )для збереження ефекту локальним . Коли вбудований запускаються без аргументів, вона не замінює поточну оболонку з новим процесом, але замість цього застосовує яке - або з його перенаправлень до поточної оболонці.exec

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

З кодом:

(
    # Suppress silly error messages unless running from terminal
    case $fCron in true) exec 2>/dev/null;; esac

    google-chrome --headless --disable-gpu --dump-dom \
        "$RobWebAddress" > "$DownloadName"
)

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

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


FYI SteelDriver згадав щось про execне знаю, чи планує він відповідь на це ...
WinEunuuchs2Unix

@ WinEunuuchs2Unix Я сподіваюся, що така відповідь ще розміщена. Хоча я в основному рекомендую використовувати функцію, я також включив метод, що включає перенаправлення на exec. Але, як я вже згадував у думках, я не охоплював більш досконалих програм, де старий дескриптор файлів зберігається та відновлюється без підпакетів. Я також не охоплював менш складні програми, як-от просто зберігати перенаправлення, якщо це кінець сценарію. Ще одна відповідь, якщо вона розміщена, може охопити і те, і, можливо, більше.
Елія Каган

Я оновив своє запитання наявним, execщо не повинно вплинути на вашу відповідь.
WinEunuuchs2Unix

@ WinEunuuchs2Unix Так, це не повинно бути проблем. Я додав абзац про це до кінця відповіді.
Елія Каган

Цікаве одкровення, читаючи вашу відповідь, RobWebAddress- це, безумовно, глобальний контекст. DownloadNameвизначався локальним, але повинен бути глобальним. Чомусь дочірні функції успадковують місцеві визначення батьків (для дотепності DownloadNameбуло видно DownloadAsHTML ()функцію, яку називали UpdateOne ()функцією, яка визначила її як локальну. Це був грубий день :(
WinEunuuchs2Unix

4

Чому жорстко кодований аргумент працює, але не є аргументом як змінною?

Оскільки елементи синтаксису не інтерпретуються із значень розширеної змінної. Тобто розширення змінної не те саме, що замінити посилання змінної текстом змінної в командному рядку. ( Такі речі , як ;, |, &&і цитати і т.д., також не спец в значеннях змінних.)

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

Псевдоніми - це лише заміна тексту, тому вони можуть містити синтаксичні елементи, наприклад оператори та ключові слова. У сценарії вам знадобиться shopt expand_aliases, оскільки за замовчуванням вони відключені в неінтерактивних оболонках. Отже, це друкує 2(лише):

#!/bin/bash
shopt -s expand_aliases

alias redir='> /dev/null'
redir echo 1
alias redir=''
redir echo 2

(І ви також можете, alias jos=if niin=then soj=fiа потім написати всі свої if-заяви фінською мовою. Я впевнений, що хтось, хто читає сценарій, любив би вас.)

Як варіант, пишіть перенаправлення завжди, але керуйте лише ціллю змінною. Вам потрібна ціль без операції для випадку, коли ви не хочете змінювати, куди йде результат, але /dev/stderrв цьому випадку має працювати. Насправді, додавання 2> /dev/stderrне є опціоном через те, як Linux ставиться до файлу fd's /proc/<pid>/fdяк незалежного від оригіналу. Це впливає на позиціонування позиції запису і зіпсує вихід, якщо він перейде до звичайного файлу.

Він повинен працювати в режимі додавання (хоча якщо stderr переходить до труби або до терміналу):

#!/bin/sh
exec 2>/tmp/error.log
dst=/dev/null
ls -l /nosuchfile-1 2>> "$dst"     # this doesn't print
dst=/dev/stderr
ls -l /nosuchfile-2 2>> "$dst"
ls -l /nosuchfile-3 2>> "$dst"

Так що повторити: 2> /dev/stderrможе зламатися.


Ха-ха-ха, я зараз буду використовувати лише фінські ifs на роботі. :>
десерт

Мені подобається альтернативна пропозиція. Думка про expand_aliasesце страшна, бо ~/.bashrcя думаю, що ваша програма може бути заручницею .
WinEunuuchs2Unix

1
@ WinEunuuchs2Unix, так, expand_aliasesце трохи страшно. Але ~/.bashrcце не повинно бути проблемою, оскільки його читають лише інтерактивні оболонки, .profileа друзі, які можуть назвати це, читаються лише оболонками для входу. Неінтерактивні оболонки без входу, як сценарії, не повинні запускати жодну з них. (Але тоді є $BASH_ENV, і, мабуть .bashrc, читається, якщо stdin підключений до мережевого сокету. Наскільки він може
переплутатись

Ну я чудово розумію вашу альтернативну пропозицію, і я спробую сьогодні ввечері :)
WinEunuuchs2Unix

Чесно кажучи, я не впевнений, в який спосіб я б це здійснив, якби мені довелося. Я, мабуть, зберігаю команду у функції чи масиві, а потім гілку, щоб вирішити, чи слід перенаправляти туди (використання функції було показано в іншій відповіді). Або цей 2>> "$dst"трюк, але я просто зрозумів, що він не працює в загальному випадку, тому краще будьте обережні.
ilkkachu

1

Назва питання: "Як передати 2> / dev / null як змінну?" Це фактично можна зробити за допомогоюeval

joshua@nova:/tmp$ X=">/dev/null"
joshua@nova:/tmp$ echo $X
>/dev/null
joshua@nova:/tmp$ eval echo $X
joshua@nova:/tmp$ eval echo hi
hi
joshua@nova:/tmp$ eval echo hi $X
joshua@nova:/tmp$ echo hi $X
hi >/dev/null
joshua@nova:/tmp$ 

Тож ми можемо переписати як

# Hide irrelevant errors so chrome doesn't email us in cron
local HideErrors
local RobWebAddress2
local DownloadName2
[[ $fCron == true ]] && HideErrors="2>/dev/null"
RobWebAddress2='"$RobWebAddress"'
DownloadName2='>"$DownloadName"'

eval google-chrome --headless --disable-gpu --dump-dom \
    $RobWebAddress2 $DownloadName2 "$HideErrors"

Якщо непрямий доступ до змінної перешкоджає розширенню надто рано в решті командного рядка.

Подвійні лапки у змінних працюють чудово.

joshua@nova:/tmp$ X='"'
joshua@nova:/tmp$ Y='$X'
joshua@nova:/tmp$ eval echo $Y
"
joshua@nova:/tmp$ 

@EliahKagan: Назва питання: "Як передати 2> / dev / null як змінну?"
Джошуа

Гаразд, це не працювало. Я це виправив.
Джошуа

Тепер файл завжди називається DownloadName- а RobWebAddressдля URL-адреси завжди використовується буквальний текст . Ви використовуєте $" "цитування . Я думаю, що це може бути ненавмисним, і ви, можливо, захочете входити $всередину " ", але ви зробили це в обох місцях, тому я не впевнений. Я думаю, що просто > "$DownloadName"слід це виправити. Але я розумію, що вам це може не сподобатися, оскільки випадкове змішання аргументів і неаргументів з evalоднією з причин, це настільки небезпечно і широко не рекомендується використовувати спонукальну evalповедінку.
Елія Каган

@EliahKagan: О. У моїй переважній оболонці для сценаріїв немає "" "котирування.
Джошуа

1
Якщо ви виправите це, воно має спрацювати. І я завжди помилявся, думаючи, що це наклеює цитати на довільний текст! Він будує буквальні аргументи, які evalоб'єднують перед оцінкою. Але я думаю, що інший спосіб сказати це - це заплутаний спосіб написання, eval 'google-chrome --headless --disable-gpu --dump-dom "$RobWebAddress" > "$DownloadName" '"$HideErrors"що нагадує зовнішній вигляд коду ОП. І взагалі, використовувати evalдля завдань, які не потребують, це погано . (Нічого з цього виправдання - ані навіть пояснення - неправильність та ворожість моєї старої відповіді.)
Елія Каган
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.