Değişken olarak 2> / dev / null nasıl iletilir?


13

Çalışan bu kod var:

# 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

Eğer bu şekilde kısaltmaya çalışırsam:

# 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"

Hata mesajları alıyorum:

[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 ...)

Sabit kodlu bir argüman neden değişken olarak bir argüman olarak çalışmıyor?


Düzenleme 2:

Şu anda ikinci cevabın alternatif önerisiyle başarı buldum:

# 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"

Düzenleme 1:

İlk cevaba dayanarak, program başlığının zaten içerdiğini belirtmeliyim:

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

Bunun [[ $fCron == true ]] && exec 2>/dev/nullyerine deneyebilirsiniz
steeldriver

kabaca konuşursak, bunun nedeni kabuğun değişkenleri genişletmeden önce yeniden yönlendirmeler ayarlamasıdır. Örneğin bkz. Bash: stderr | stdout redirection
steeldriver'ı

Yanıtlar:


19

Genişleterek yeniden yönlendirmenin oluşmamasının nedeni , parametre genişletmeleri tarafından üretildikten sonra "$HideErrors"gibi sembollerin >özel olarak işlem görmemesidir . Bu aslında çok iyidir, çünkü bu semboller metinde görünür ve tam anlamıyla genişletmek ve kullanmak isteyebilirsiniz.

Bu, siz teklif edip etmediğinizi içerir $HideErrors. Parametre genişletmenin sonucu, genişletme belirtilmediğinde sözcük bölme ve globbing işlemine tabidir , ancak hepsi bu kadar.


Bu konuda ne yapılacağı konusunda, koşullu yeniden yönlendirme elde etmenin birçok yolu vardır. Çok basit bir komut için, tüm komutu bir caseveya if- elseyapının her dalına bir kez olmak üzere iki kez yazmak mantıklı olabilir . Ancak bu kısa sürede külfetli hale gelir ve gösterdiğiniz komut kesinlikle bunun ideal olmayacağı bir durumdur.

Kendinizi tekrar etmekten kaçınmanıza izin veren yaklaşımlardan, özellikle tavsiye ettiğim iki tane var, çünkü bunlar oldukça temiz ve doğru olması kolaydır. Aynı komut ve yönlendirme için bir kerede değil, bunlardan sadece birini kullanmak istersiniz.

Yönlendirme yerine komutu saklayın. Yönlendirmeyi bir değişkende depolamaya çalışmak ve parametre genişletmesini uygulamak yerine, komutu bir kabuk işlevinde saklayın . Daha sonra , bir daldaki yönlendirme ile ve diğer daldaki yönlendirme olmadan işlevin çağrıldığı bir caseveya if- yazın else.

Komutunuzu bir kez yazmak istediğiniz ancak birden fazla koşulda çalıştırmak istediğiniz kod olarak kavramsallaştırırsanız, bir işlev doğal çözümdür. Ben genellikle bunu yaparım. Ne bir alt kabuk ne de manuel depolama ve durumun sıfırlanması gerekmemektedir .

Kodunuzla:

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

İstediğiniz boşluğu veya isterseniz if- uygulayabilirsiniz else. Bash, dinamik olarak kapsamlandırılan çoğu programlama dilinin aksine, Bash dinamik olarak kapsamlandığından , launcharayanın RobWebAddressve DownloadNamedeğişkenlerin otomatik olarak kullanıldığını unutmayın .

Komutu bir alt kabukta çalıştırın ve yeniden yönlendirmeyi şuraya uygulayın exec. Bu ne hakkında yorumladı steeldriver ama içeride ( )lokal etkisini tutmak . Ne zaman yerleşik bağımsız değişken olmadan çalıştırılır, yani yeni bir süreçle şimdiki kabuğu yerine, ancak bunun yerine geçerli kabuk onun yönlendirmeler herhangi geçerlidir etmez.exec

(Ayrıca, bir alt kabuk kullanmadan ve dolayısıyla mevcut kabuğun ortamını değiştirme yeteneğinden ödün vermeden standart hatanın ne olduğunu takip etmek ve geri yüklemek de mümkündür. Bununla birlikte, bunun ayrıntılarını diğer cevaplara bırakacağım.

Kodunuzla:

(
    # 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"
)

Kapanıştan sonra ), standart hata aslında daha önce olduğu her şeye geri yüklenir, çünkü üst kabukta değil, yalnızca alt kabukta yeniden yönlendirilir. Alt kabuklar bunların bir kopyasını aldığından, bu da mevcut kabuk değişkenleriyle iyi çalışır. Bir kabuk işlevi kullanmayı tercih etsem de, bu yöntemin daha az kod gerektirebileceğini kabul ediyorum.

Her iki yöntem de, koşullu davranışı içeren kodu çağıran kabuk işlevlerine uygulanan yeniden yönlendirme ve standart hatanın tüm komut dosyası zaten bir önceki veya tarafından yönlendirilmişti . Yolun süreç ikamesi ile üretilmesi sorun değil.exec 2>&fdexec 2> path


FYI SteelDriver exec, bu konuda bir cevap planlayıp planlamadığını bilmediğinden bahsetti ...
WinEunuuchs2Unix

@ WinEunuuchs2Unix Umarım böyle bir cevap hala gönderilir. Esas olarak bir işlev kullanmanızı öneririm, ancak yeniden yönlendirmeyi içeren bir yöntem de ekledim exec. Ancak, parantez içinde belirttiğim gibi, eski dosya tanımlayıcısının alt kabuk olmadan tutulduğu ve geri yüklendiği daha karmaşık uygulamaları kapsamadım. Ayrıca , komut dosyasının sonu ise yeniden yönlendirmeyi tutmak gibi daha az karmaşık uygulamaları da kapsamadım . Gönderilirse başka bir cevap her ikisini ve belki de daha fazlasını kapsayabilir.
Eliah Kagan

execSorumu, bence hiç yanıtınızı etkilememesi gereken bir varlıkla güncelledim .
WinEunuuchs2Unix

@ WinEunuuchs2Unix Evet, sorun olmamalı. Cevabın sonuna bununla ilgili bir paragraf ekledim.
Eliah Kagan

Cevabınızı okuyan ilginç vahiy RobWebAddresskesinlikle küresel bir bağlam. DownloadNameyerel olarak tanımlandı, ancak küresel bağlam olmalıdır. Bazı nedenlerden dolayı çocuk işlevleri, ebeveynlerin yerel tanımlarını devralır (zekâ DownloadName, yerel olarak tanımlanan işlev DownloadAsHTML ()tarafından çağrılan işlevle görülebilir UpdateOne (). Zor bir gündü :(
WinEunuuchs2Unix

4

Sabit kodlu bir argüman neden değişken olarak bir argüman olarak çalışmıyor?

Çünkü sözdizimi öğeleri genişletilmiş değişken değerlerden yorumlanmaz. Yani, değişken genişletmesi, değişken başvurusunu komut satırındaki değişkenin metniyle değiştirmekle aynı şey değildir. (Stuff gibi ;, |, &&vb ve tırnak da değişkenlerin değerlerinde özel değildir.)

Yapabileceğiniz şey takma adları kullanmak veya değişkeni yalnızca yeniden yönlendirmenin hedefini tutmak için kullanmaktır.

Adlar vardır onlar çok, sadece bir metin değiştirme yapabilirsiniz operatörleri ve anahtar kelimeler gibi sözdizimsel öğeleri tutun. Bir komut dosyasında, shopt expand_aliasesvarsayılan olarak etkileşimli olmayan kabuklarda devre dışı bırakıldıkları için yapmanız gerekir . Yani, bu yazdırılır 2(yalnızca):

#!/bin/bash
shopt -s expand_aliases

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

(Ayrıca alias jos=if niin=then soj=fitüm if ifadelerinizi Fince olarak da yazabilirsiniz. Eminim senaryoyu okuyan herkes sizi sevecektir.)

Alternatif olarak, yönlendirmeyi her zaman yazın, ancak yalnızca hedefi bir değişkenle kontrol edin. Çıkış nereye gittiğini gerçi sen, değiştirmek istemediğiniz durum için no-op hedefinizin de olması gerekir ama /dev/stderrbu durumda çalışmalıdır. Aslında, ekleme 2> /dev/stderr, Linux'un açılmış fd'leri /proc/<pid>/fdorijinalden bağımsız olarak ele alma biçimi nedeniyle bir işlem değildir . Bu yazma konumunun konumunu etkiler ve normal bir dosyaya giderse çıktıyı bozar.

Yine de ekleme modunda çalışmalıdır (veya stderr bir boruya veya terminale giderse):

#!/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"

Tekrarlamak için: 2> /dev/stderrkırılabilir.


Hahaha, sadece Finlandiya'yı işten sonra kullanacağım. :>
tatlı

Alternatif öneriyi seviyorum. Düşüncesi expand_aliasesile programınız bekletilen rehine olabilir çünkü korkutucu ~/.bashrcsanırım.
WinEunuuchs2Unix

1
@ WinEunuuchs2Unix, evet, expand_aliasesbiraz korkutucu. Ancak ~/.bashrcbir sorun olmamalı, çünkü sadece etkileşimli kabuklar tarafından okunabilir ve .profileve buna çağırabilecek arkadaşlar sadece giriş kabukları tarafından okunur. Komut dosyaları gibi etkileşimli olmayan oturum açma kabukları bunlardan hiçbirini çalıştırmamalıdır. (Ama sonra orada $BASH_ENVve görünüşe göre .bashrcstdin bir ağ soketine bağlıysa okunur. Nasıl kıvrık olabilir ...)
ilkkachu

Alternatif önerinizi mükemmel anlıyorum ve bu gece deneyeceğim :)
WinEunuuchs2Unix

Dürüst olmak gerekirse, bunu hangi şekilde uygulayacağımdan emin değilim. Muhtemelen komutu bir işlevde veya dizide saklar ve sonra yönlendirmeyi oraya koyup koymayacağınıza karar vermek için (bir işlevi kullanarak diğer yanıtta gösterilmiştir). Ya da bu 2>> "$dst"hile, ama genel durumda işe yaramadığını fark ettim, bu yüzden dikkatli olsanız iyi olur.
ilkkachu

1

Soru başlığı: "2> / dev / null değişken olarak nasıl iletilir?" Bu aslında kullanılarak yapılabilireval

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$ 

Böylece yeniden yazabiliriz

# 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"

Dolaylı değişken erişim komut satırının geri kalanında genişlemenin çok erken gerçekleşmesini engelliyorsa.

Değişkenlerdeki çift tırnak işaretleri gayet iyi çalışıyor.

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

@EliahKagan: Soru başlığı: "Değişken olarak 2> / dev / null nasıl iletilir?"
Joshua

Tamam işe yaramıyordu. Ben tamir ettim.
Joshua

Artık dosya her zaman adlandırılır - ve URL için DownloadNamedeğişmez metin RobWebAddressher zaman kullanılır. $" "Alıntı yapıyorsunuz . Bence bu kasıtsız olabilir ve $içinde s isteyebilirsiniz " ", ama her iki yerde de bu şekilde yaptın, bu yüzden emin değilim. Bence > "$DownloadName"düzeltmeliyim. Ancak bunu beğenmeyeceğinizi anlıyorum, çünkü argümanları ve argümanları yanlışlıkla karıştırmak eval, eval'birleştirici davranışını kullanmak için çok tehlikeli ve yaygın olarak cesaret kırılmış bir nedendir .
Eliah Kagan

@EliahKagan: Oh. Komut dosyası oluşturmak için tercih ettiğim kabukta $ "" alıntı yok.
Joshua

1
Bunu düzeltirseniz, işe yaramalıdır. Ve her zaman keyfi metne alıntı yapıştırdığını düşünmek yanlıştı! Değerlendirmeden önce bir araya getirilen değişmez argümanlar oluşturur eval. Ama bunu koymanın başka bir yolu , OP kodunun görünümüneeval 'google-chrome --headless --disable-gpu --dump-dom "$RobWebAddress" > "$DownloadName" '"$HideErrors" benzeyen gizli bir yol olduğunu düşünüyorum . Ve genel olarak, ihtiyacı olmayan görevler için kullanmak kötüdür . (Bu mazeretin hiçbiri - hatta açıklamıyor - eski cevabımın yanlışlığı ve düşmanlığı.)eval
Eliah Kagan
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.