Okuyucunun yararı için bu tarif burada
- stderr'ı bir değişkene yakalamak için oneliner olarak tekrar kullanılabilir
- hala komutun dönüş koduna erişim sağlıyor
- Geçici bir dosya tanımlayıcı 3'ü feda eder (elbette sizin tarafınızdan değiştirilebilir)
- Ve bu geçici dosya tanımlayıcılarını iç komuta maruz bırakmaz
Eğer içine stderr
bazılarını yakalamak istiyorsanız yapabilirsinizcommand
var
{ var="$( { command; } 2>&1 1>&3 3>&- )"; } 3>&1;
Daha sonra hepsine sahipsiniz:
echo "command gives $? and stderr '$var'";
Eğer command
basittir (gibi bir şey değil a | b
) Eğer iç bırakabilir {}
koyma:
{ var="$(command 2>&1 1>&3 3>&-)"; } 3>&1;
Kolay bir yeniden kullanılabilir bash
fonksiyona sarılmış (muhtemelen sürüm 3 ve üstü için gereklidir local -n
):
: catch-stderr var cmd [args..]
catch-stderr() { local -n v="$1"; shift && { v="$("$@" 2>&1 1>&3 3>&-)"; } 3>&1; }
Açıklaması:
local -n
takma adlar "$ 1" (için değişken olan catch-stderr
)
3>&1
stdout noktalarını kaydetmek için dosya tanımlayıcı 3'ü kullanır
{ command; }
(veya "$ @") sonra çıkış yakalama içinde komutu yürütür $(..)
- Burada tam siparişin önemli olduğunu lütfen unutmayın (yanlış şekilde yapmak dosya tanımlayıcılarını yanlış karıştırır):
2>&1
stderr
çıktı yakalamaya yönlendiriyor$(..)
1>&3
dosya tanımlayıcı 3'te kaydedilen "dış" a geri çeken stdout
çıkıştan yeniden yönlendirir. FD 1'in daha önce nereye işaret ettiğini hala not edin : Çıktı yakalamaya$(..)
stdout
stderr
$(..)
3>&-
daha sonra gerekmediği için dosya tanımlayıcısını 3 kapatır, öyle ki command
aniden bilinmeyen bazı açık dosya tanımlayıcıları görünmez. Dış kabuğun hala FD 3 açık olduğunu, ancak command
görmeyeceğini unutmayın.
- İkincisi önemlidir, çünkü bazı programlar
lvm
beklenmedik dosya tanımlayıcılarından şikayet ediyor. Ve lvm
şikayet ediyor stderr
- sadece ne yakalamak olacak!
Uygun şekilde uyarlanırsanız, bu tarifle başka bir dosya tanımlayıcı yakalayabilirsiniz. Tabii ki dosya tanımlayıcı 1 hariç (burada yönlendirme mantığı yanlış olurdu, ancak dosya tanımlayıcı 1 için var=$(command)
her zamanki gibi kullanabilirsiniz ).
Bunun dosya tanımlayıcı 3'ü feda ettiğini unutmayın. Dosya tanımlayıcıya ihtiyacınız olursa, numarayı değiştirmekten çekinmeyin. Ama (1980 itibaren) bazı kabukları anlayabiliriz bilin 99>&1
argüman olarak 9
takip 9>&1
(bunun için hiçbir sorun bash
).
Ayrıca, bu FD 3'ü bir değişken üzerinden yapılandırılabilir hale getirmenin kolay olmadığını da unutmayın. Bu, işleri çok okunmaz hale getirir:
: catch-var-from-fd-by-fd variable fd-to-catch fd-to-sacrifice command [args..]
catch-var-from-fd-by-fd()
{
local -n v="$1";
local fd1="$2" fd2="$3";
shift 3 || return;
eval exec "$fd2>&1";
v="$(eval '"$@"' "$fd1>&1" "1>&$fd2" "$fd2>&-")";
eval exec "$fd2>&-";
}
Güvenlik notu: İlk 3 argüman catch-var-from-fd-by-fd
bir 3. taraftan alınmamalıdır. Onları her zaman açıkça "statik" bir şekilde verin.
Yani hayır-hayır-hayır catch-var-from-fd-by-fd $var $fda $fdb $command
, bunu asla yapma!
Bir değişken değişken adı iletirseniz, en azından aşağıdaki gibi yapın:
local -n var="$var"; catch-var-from-fd-by-fd var 3 5 $command
Bu yine de sizi her istismardan korumaz, ancak en azından yaygın komut dosyası hatalarını tespit etmeye ve önlemeye yardımcı olur.
Notlar:
catch-var-from-fd-by-fd var 2 3 cmd..
aynıdır catch-stderr var cmd..
shift || return
doğru sayıda argüman vermeyi unutmanız durumunda çirkin hataları önlemenin bir yoludur. Belki de kabuğun sonlandırılması başka bir yol olacaktır (ancak bu komut satırından test yapmayı zorlaştırır).
- Rutin öyle yazılmıştır ki, anlaşılması daha kolaydır. İşlev gerekli olmayacak şekilde yeniden yazılabilir
exec
, ancak daha sonra gerçekten çirkinleşir.
- Bu rutin olmayan için tekrar yazılabilir
bash
gerek olmadığı gibi bildik local -n
. Ancak o zaman yerel değişkenleri kullanamazsınız ve çok çirkinleşir!
- Ayrıca
eval
s güvenli bir şekilde kullanıldığını unutmayın . Genellikle eval
tehlikeli kabul edilir. Ancak bu durumda "$@"
(keyfi komutları çalıştırmak için) kullanmaktan daha kötü değildir . Ancak lütfen burada gösterildiği gibi tam ve doğru alıntıları kullandığınızdan emin olun (aksi takdirde çok çok tehlikeli hale gelir ).
ERROR=$(./useless.sh | sed 's/Output/Useless/' 2>&1 1>/dev/ttyX)