변수로 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

.. 대략적으로 말하면, 쉘이 변수를 확장하기 전에 리디렉션을 설정하기 때문입니다. 예를 참조하십시오 배쉬 : 사용에게 변수를 저장 stderr로 | 표준 출력 재
steeldriver

답변:


19

확장으로 인해 리디렉션이 발생하지 않는 이유 "$HideErrors"매개 변수 확장> 으로 생성 된 후 와 같은 기호 가 특수하게 처리되지 않기 때문 입니다. 이러한 기호는 텍스트로 표시되어 확장하여 문자 그대로 사용할 수 있기 때문에 실제로 매우 좋습니다.

이것은 당신이 인용 여부를 보유하고 있습니다 $HideErrors. 확장이 인용되지 않은 경우 매개 변수 확장의 결과는 단어 분할 및 글 로빙에 영향을 받지만 그게 전부입니다.


그것에 대해 무엇을 해야하는지에 관해서는 조건부 리디렉션을 달성하는 많은 방법이 있습니다. 매우 간단한 명령의 경우 case, if- else구문 의 각 분기에 한 번 전체 명령을 두 번 작성하는 것이 합리적 일 수 있습니다 . 그러나 이것은 곧 부담이되며, 당신이 보여준 명령은 확실히 이상적이지 않은 경우입니다.

스스로 반복 하는 것을 피할 수있는 접근법 중에서, 특히 깨끗하고 올바르게 얻을 수 있기 때문에 내가 추천하는 두 가지가 있습니다. 동일한 명령과 리디렉션을 위해 한 번에 하나만 사용하는 것이 좋습니다.

리디렉션 대신 명령을 저장하십시오. 변수에 경로 재 지정을 저장하고 매개 변수 확장을 적용하는 대신 명령을 쉘 함수 에 저장하십시오 . 그런 다음 한 분기에서 리디렉션을 사용하고 다른 분기에서는 기능을 사용하지 않고 함수를 호출 하는 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당신이 선호하는 대신합니다. Bash는 어휘 범위가 지정된 대부분의 프로그래밍 언어와 달리 동적으로 범위지정 되므로 로컬 변수 인 경우에도 launch호출자 RobWebAddressDownloadName변수 를 자동으로 사용합니다 .

서브 쉘에서 명령을 실행하고에 경로 재 지정을 조건부로 적용하십시오 exec. 이것은 스틸 드라이버가 언급 한 내용 이지만 , 효과를 로컬로 유지하기 위해 내부 ( ) 있습니다. 내장 이 인수없이 실행될 때 ,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


참고로 SteelDriver이에 대해 뭔가 언급 exec... 그는 그것에 대한 답을 계획 않다면 모르겠어요
WinEunuuchs2Unix

@ WinEunuuchs2Unix 그런 답변이 여전히 게시되기를 바랍니다. 주로 함수 사용을 권장하지만에 대한 리디렉션과 관련된 방법도 포함했습니다 exec. 그러나 괄호로 언급했듯이 이전 파일 설명자가 하위 셸없이 유지되고 복원되는보다 정교한 응용 프로그램은 다루지 않았습니다. 또한 스크립트의 끝인 경우 리디렉션을 유지하는 것과 같이 복잡한 응용 프로그램 도 다루지 않았습니다 . 게시 된 다른 답변은 둘 다를 포함 할 수 있습니다.
Eliah Kagan

기존 질문으로 내 질문을 업데이트했는데, exec귀하의 답변에 전혀 영향을 미치지 않습니다.
WinEunuuchs2Unix

@ WinEunuuchs2Unix 예, 문제 없습니다. 답변 끝에 단락을 추가했습니다.
Eliah Kagan

당신의 답을 읽는 흥미로운 계시는 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-statement를 핀란드어로 쓸 수 있습니다 . 대본을 읽는 사람이라면 누구나 당신을 사랑할 것입니다.)

또는 항상 리디렉션을 작성하되 변수로 대상 만 제어하십시오. 출력이 진행되는 위치를 변경하지 않으려는 경우에는 작동하지 않는 대상이 필요 하지만 /dev/stderr그 경우에는 작동해야합니다. 실제로, 2> /dev/stderr리눅스가 fd의 열린 파일을 /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깨질 수 있습니다.


하하하, 지금부터는 직장에서 핀란드어 만 사용합니다. :>
디저트

나는 다른 제안을 좋아한다. 내가 생각 expand_aliases하기에 프로그램을 인질로 잡을 수 있기 때문에 생각 이 무섭다 ~/.bashrc.
WinEunuuchs2Unix

1
@ WinEunuuchs2Unix expand_aliases는 약간 무섭습니다. 그러나 ~/.bashrc대화식 쉘에서만 읽을 수 있기 때문에 문제가되지 않아야하며 .profile호출 할 수있는 친구는 로그인 쉘에서만 읽을 수 있습니다. 스크립트와 같은 비 대화식 비 로그인 셸은 실행하지 않아야합니다. (그러나 stdin이 네트워크 소켓에 연결되어 있으면 $BASH_ENV분명히 .bashrc읽혀집니다. 얼마나 복잡한 지 알 수 있습니다 ...)
ilkkachu

글쎄, 나는 당신의 대체 제안을 완벽하게 이해하고 오늘 밤 그것을 시도 할 것이다 :)
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을 변수로 전달하는 방법?"
Joshua

좋아, 작동하지 않았다. 나는 그것을 고쳤다.
Joshua

이제 파일 이름이 항상 지정 DownloadName되고 리터럴 텍스트 RobWebAddress가 항상 URL에 사용됩니다. $" "따옴표를 사용 하고 있습니다. 나는 이것이 의도하지 않은 것이라고 생각하고 $s 내부를 원할 수도 " "있지만 두 곳에서 그렇게 했으므로 확실하지 않습니다. 그냥 > "$DownloadName"고쳐야 한다고 생각 합니다. 그러나 우연히 논란과 논증을 섞는 eval것이 하나의 이유이기 때문에 eval연쇄 행동 을 사용하는 것이 너무 위험하고 널리 권장되지 않기 때문 입니다.
Eliah Kagan

@EliahKagan : 아. 스크립팅에 선호하는 셸에는 $ ""따옴표가 없습니다.
Joshua

1
고치면 작동합니다. 그리고 나는 그것이 임의의 텍스트에 따옴표를 붙여 넣었다고 생각하는 것이 항상 틀 렸습니다! 평가하기 전에 연결되는 리터럴 인수를 작성합니다 eval. 그러나 나는 그것을 넣는 또 다른 방법 은 OP 코드 eval 'google-chrome --headless --disable-gpu --dump-dom "$RobWebAddress" > "$DownloadName" '"$HideErrors"모양 과 비슷한 난독 화 된 작성 방법이라는 것 입니다. 그리고 일반적으로 필요하지 않은 작업에 사용 eval하는 것은 좋지 않습니다 . (이 변명 없음 -도도 설명하지 - 내 옛날 응답의 그름과 적대감을.)
엘리야 케이건
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.