Bash'teki son komutun çıktısını yeniden kullanma


Yanıtlar:


184

Sen kullanabilirsiniz $(!!) için yeniden hesaplamak (değil yeniden kullanım) son komutun çıktısı.

!!Kendi yürütür son komut.

$ echo pierre
pierre
$ echo my name is $(!!)
echo my name is $(echo pierre)
my name is pierre

9
Geri $(your_cmd)uçları kullanarak kendinizi yazmaktan kurtarabilirsiniz : `your_cmd`Gnu Bash kılavuzuna göre fonksiyonel olarak aynıdırlar. Tabii ki bu da memecs tarafından yapılan uyarıya tabidir

Uygulamada, komutların çıktısı deterministiktir, bu yüzden yeniden hesaplama üzerinde acı çekmeyeceğim. git diff $(!!)Önceki komutun bir findçağırma olduğu gibi bir şey yapmak istediğinizde kesinlikle yararlıdır .
Sridhar Sarnobat

Görünüşe göre $()backticks yerine kullanmak için iyi nedenler var . Ama muhtemelen uykuyu kaybedecek bir şey yok. github.com/koalaman/shellcheck/wiki/SC2006
Michael Crenshaw

1
@ user2065875, backbicks hala Bash'te çalışıyor olsa da, lehine reddedildi $().
kurczynski

87

Cevap hayır. Bash, belleğindeki herhangi bir parametreye veya bloğa herhangi bir çıkış tahsis etmez. Ayrıca, Bash'e yalnızca izin verilen arabirim işlemleriyle erişebilirsiniz. Bash'un özel verilerine, onu hacklemedikçe erişilemez.


2
Her komuttan önce çalışan bazı özel bash 'ara katman yazılımı' işlevine sahip olmak mümkün mü, tek yaptığı sonuç çıktıyı dahili bir pano gibi davranan bir şeye kaydetmek (örneğin, BASH_CLIPBOARD)?
Pat Needham

@PatNeedham Emin değilim. Yüklenebilir modülleri kontrol edin. Bir tane yazıp yazamayacağınıza bakın.
19:55 tarihinde konsolebox

11

Bunu yapmanın bir yolu trap DEBUG:

f() { bash -c "$BASH_COMMAND" >& /tmp/out.log; }
trap 'f' DEBUG

Şimdi en son yürütülen komutun stdout ve stderr'i /tmp/out.log

Tek dezavantajı, komutun iki kez yürütülmesidir: bir kez çıktı ve hatayı /tmp/out.lognormal bir şekilde ve bir kez yönlendirmek için . Muhtemelen bu davranışı önlemenin bir yolu vardır.


10
Bu yüzden ben yazarken rm -rf * && cd .., sadece geçerli dizini değil, aynı zamanda üst dir silinecek ?? Bu yaklaşım benim için çok tehlikeli görünüyor
phil294

1
Şimdi bu işlemi nasıl geri alabilirim?
Kartik Chauhan

5
@KartikChauhan: Just dotrap '' DEBUG
anubhava

7

Mac kullanıyorsanız ve çıktınızı bir değişkene yazmak yerine panoya depolamak sakıncası yoksa, geçici çözüm olarak pbcopy ve pbpaste kullanabilirsiniz.

Örneğin, bir dosyayı bulmak ve içeriğini başka bir dosyayla dağıtmak için bunu yapmak yerine:

$ find app -name 'one.php' 
/var/bar/app/one.php

$ diff /var/bar/app/one.php /var/bar/two.php

Bunu yapabilirsin:

$ find app -name 'one.php' | pbcopy
$ diff $(pbpaste) /var/bar/two.php

/var/bar/app/one.phpİlk komutu çalıştırdığınızda dize panodadır.

Bu arada, pb içinde pbcopyve pbpastestandı çalışma alanındaki panoya ile eşanlamlı için.


1
üzerinde lin $ find app -name 'one.php' | xclip $ diff $(xclip -o) /var/bar/two.php
si-mikey

ilginç, pbpastekomut ikamesi ile birlikte kullanmayı hiç düşünmemiştim .
Sridhar Sarnobat

İlk ve daha sonra bunun verimsiz olduğunu fark edersem, ikinci seçenekle yeniden karşılaşmadan hesaplama zamanından nasıl tasarruf edebilirim?
NelsonGon

4

Anubhava'nın cevabından esinlenerek, her komutu iki kez çalıştırdığı için aslında kabul edilemez olduğunu düşünüyorum.

save_output() { 
   exec 1>&3 
   { [ -f /tmp/current ] && mv /tmp/current /tmp/last; }
   exec > >(tee /tmp/current)
}

exec 3>&1
trap save_output DEBUG

Bu şekilde son komutun çıktısı / tmp / last olur ve komut iki kez çağrılmaz.


1
Bulduğum bir dezavantaj, komutlarınızın hiçbirinin artık bir tty'de çalışmadıklarını düşünmemesi, bu grepda git diffyine de yardımcı programlar veya +1 için çalışan renk kodları almak için özel dikkat göstermeniz gerektiği anlamına geliyor
Marty Neal

2

Konsolebox'ın dediği gibi, bash'ın kendisini hacklemelisiniz. Buraya bunun nasıl başarabileceğine dair oldukça iyi bir örnek. Stderred deposu (aslında stdout'u renklendirme amaçlı), nasıl oluşturulacağı hakkında talimatlar verir.

Denedim: İçinde yeni bir dosya tanımlayıcı .bashrcgibi

exec 41>/tmp/my_console_log

(sayı isteğe bağlıdır) ve stderred.ciçeriği fd 41'e yazacak şekilde değiştirin. çalıştı, ancak bir sürü NUL bayt, garip biçimlendirmeler içeriyor ve temelde ikili veriler, okunabilir değil. Belki de C'yi iyi anlayan biri bunu deneyebilir.

Eğer öyleyse, son yazdırılan çizgiyi elde etmek için gereken her şey tail -n 1 [logfile].


1

Evet, neden her seferinde ekstra satırlar yazıyorsunuz? Girdiye geri yönlendirebilirsiniz, ancak girişe (1> & 0) çıkış hayır olur. Ayrıca, her dosyaya tekrar tekrar bir işlev yazmak istemezsiniz.

Dosyaları hiçbir yere dışa aktarmıyorsanız ve yalnızca yerel olarak kullanmak istiyorsanız, Terminal'in işlev bildirimini ayarlamasını sağlayabilirsiniz. İşlevi ~/.bashrcdosyaya veya dosyaya eklemeniz gerekir ~/.profile. İkinci durumda, etkinleştirmeniz gerekir Run command as login shelldan Edit>Preferences>yourProfile>Command.

Basit bir işlev yapın, söyleyin:

get_prev()
{
    # option 1: create an executable with the command(s) and run it
    echo $* > /tmp/exe
    bash /tmp/exe > /tmp/out

    # option 2: if your command is single command (no-pipe, no semi-colons), still it may not run correct in some exceptions
    #echo `$*` > /tmp/out

    # return the command(s) outputs line by line
    IFS=$(echo -en "\n\b")
    arr=()
    exec 3</tmp/out
    while read -u 3 -r line
    do
        arr+=($line)
        echo $line
    done
    exec 3<&-
}

Ana komut dosyasında:

#run your command:
cmd="echo hey ya; echo hey hi; printf `expr 10 + 10`'\n' ; printf $((10 + 20))'\n'"
get_prev $cmd
#or simply
get_prev "echo hey ya; echo hey hi; printf `expr 10 + 10`'\n' ; printf $((10 + 20))'\n'"

Bash, değişkeni önceki kapsamın dışında bile kaydeder:

#get previous command outputs in arr
for((i=0; i<${#arr[@]}; i++)); do echo ${arr[i]}; done

1

Çok Basit Çözüm

Yıllarca kullandığım.

Senaryo (eklemek sizin .bashrcya .bash_profile)

# capture the output of a command so it can be retrieved with ret
cap () { tee /tmp/capture.out}

# return the output of the most recent command that was captured by cap
ret () { cat /tmp/capture.out }

kullanım

$ find . -name 'filename' | cap
/path/to/filename

$ ret
/path/to/filename

| capTüm komutlarımın sonuna ekleme eğilimindeyim . Bu şekilde bulduğumda metin işleme yapmak için yavaş çalışan bir komutun çıktısını her zaman alabilirim res.


Ne anlamı var cat -içinde cap () { cat - | tee /tmp/capture.out}burada? cap () { tee /tmp/capture.out }Kaçırdığım bir hıçkırık var mı ?
Watson

1
@Watson iyi bir nokta! Neden orada olduğunu bilmiyorum, muhtemelen alışkanlık. cat - | cat - | cat -Daha ilginç hale getirmek için önceden eklemem gerekir mi? 😉 Gerçekten bir spandrel olduğundan emin olmak için test ettikten sonra düzeltirim
Connor

0

Bunun için tam olarak neye ihtiyaç duyduğunuzdan emin değilim, bu nedenle bu cevap alakalı olmayabilir. Bir komutun çıktısını her zaman kaydedebilirsiniz:netstat >> output.txt ancak aradığınız şeyin bu olduğunu sanmıyorum.

Tabii ki programlama seçenekleri var; bu komut çalıştırıldıktan sonra yukarıdaki metin dosyasını okuyabilmeniz ve bir değişkenle ilişkilendirebilmeniz için bir program elde edebilirsiniz ve benim tercih ettiğim dil olan Ruby'de 'backticks'i kullanarak komut çıktısından bir değişken oluşturabilirsiniz:

output = `ls`                       #(this is a comment) create variable out of command

if output.include? "Downloads"      #if statement to see if command includes 'Downloads' folder
print "there appears to be a folder named downloads in this directory."
else
print "there is no directory called downloads in this file."
end

Bunu bir .rb dosyasına yapıştırın ve çalıştırın: ruby file.rbve komuttan bir değişken oluşturur ve onu değiştirmenize izin verir.


9
"Tam olarak bunun için neye ihtiyacın olduğundan emin değilim" Peki, demek istediğim (yani demek istediğim ) sadece uzun süreli bir komut dosyası çalıştırdım, çıktıyı aramak istiyorum ve çalıştırmadan önce çıktıyı yönlendirmeyi aptalca unuttum. Şimdi betiği yeniden çalıştırmadan aptallığımı düzeltmek istiyorum.
jscs

0

Hemen uygulamaya çalışacak vaktim olmadığına dair bir fikrim var.

Peki ya aşağıdaki gibi bir şey yaparsanız:

$ MY_HISTORY_FILE = `get_temp_filename`
$ MY_HISTORY_FILE=$MY_HISTORY_FILE bash -i 2>&1 | tee $MY_HISTORY_FILE
$ some_command
$ cat $MY_HISTORY_FILE
$ # ^You'll want to filter that down in practice!

ES arabelleklemesi ile ilgili sorunlar olabilir. Ayrıca dosya çok büyük olabilir. Kişi bu sorunlara bir çözüm bulmalıdır.


0

Bence script komutunu kullanmak yardımcı olabilir. Gibi bir şey,

  1. script -c bash -qf Instagram Hesabındaki Resim ve Videoları fifo_pid

  2. Ayrıştırmadan sonra ayarlamak için bash özelliklerini kullanma.


0

Önceki komutu yeniden hesaplamak istemiyorsanız, geçerli terminal arabelleğini tarayan bir makro oluşturabilir, son komutun -supposed- çıktısını tahmin etmeye çalışır, panoya kopyalar ve son olarak da terminale yazar.

Tek bir çıktı satırı döndüren basit komutlar için kullanılabilir (Ubuntu 18.04 ile test edilmiştir gnome-terminal).

Aşağıdaki araçları yükleyin: xdootool, xclip,ruby

Adresine gnome-terminalgidin Preferences -> Shortcuts -> Select allve ayarlayın Ctrl+shift+a.

Aşağıdaki yakut komut dosyasını oluşturun:

cat >${HOME}/parse.rb <<EOF
#!/usr/bin/ruby
stdin = STDIN.read
d = stdin.split(/\n/)
e = d.reverse
f = e.drop_while { |item| item == "" }
g = f.drop_while { |item| item.start_with? "${USER}@" }
h = g[0] 
print h
EOF

Klavye ayarlarına aşağıdaki klavye kısayolunu ekleyin:

bash -c '/bin/sleep 0.3 ; xdotool key ctrl+shift+a ; xdotool key ctrl+shift+c ; ( (xclip -out | ${HOME}/parse.rb ) > /tmp/clipboard ) ; (cat /tmp/clipboard | xclip -sel clip ) ; xdotool key ctrl+shift+v '

Yukarıdaki kısayol:

  • geçerli terminal arabelleğini panoya kopyalar
  • son komutun çıktısını alır (sadece bir satır)
  • mevcut terminale yazar
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.