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 stderrbazılarını yakalamak istiyorsanız yapabilirsinizcommandvar
{ var="$( { command; } 2>&1 1>&3 3>&- )"; } 3>&1;
Daha sonra hepsine sahipsiniz:
echo "command gives $? and stderr '$var'";
Eğer commandbasittir (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 bashfonksiyona 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 -ntakma 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>&1stderrçıktı yakalamaya yönlendiriyor$(..)
1>&3dosya 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$(..)stdoutstderr$(..)
3>&-daha sonra gerekmediği için dosya tanımlayıcısını 3 kapatır, öyle ki commandaniden bilinmeyen bazı açık dosya tanımlayıcıları görünmez. Dış kabuğun hala FD 3 açık olduğunu, ancak commandgörmeyeceğini unutmayın.
- İkincisi önemlidir, çünkü bazı programlar
lvmbeklenmedik 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>&1argüman olarak 9takip 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-fdbir 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 || returndoğ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
bashgerek olmadığı gibi bildik local -n. Ancak o zaman yerel değişkenleri kullanamazsınız ve çok çirkinleşir!
- Ayrıca
evals güvenli bir şekilde kullanıldığını unutmayın . Genellikle evaltehlikeli 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)