Stdin'in son satırını tüm stdin'e ekle


9

Şu komut dosyasını düşünün:

tmpfile=$(mktemp)

cat <<EOS > "$tmpfile"
line 1
line 2
line 3
EOS

cat <(tail -1 "$tmpfile") "$tmpfile"

Bu çalışır ve çıktılar:

line 3
line 1
line 2
line 3

Diyelim ki gerçek bir dosya olmak yerine girdi kaynağımız stdin:

cat <<EOS | # what goes here now?
line 1
line 2
line 3
EOS

Komutu nasıl değiştiririz:

cat <(tail -1 "$tmpfile") "$tmpfile"

Öyleyse hala bu farklı bağlamda aynı çıktıyı üretiyor mu?

NOT: Katladığım spesifik Heredoc'un yanı sıra bir Heredoc'un kullanımı da sadece açıklayıcıdır. Kabul edilebilir herhangi bir cevap, stdin yoluyla rasgele veri aldığını varsaymalıdır .


1
stdin her zaman "gerçek bir dosya" dır (fifo / socket / etc de bir dosyadır; tüm dosyalar aranmaz). Sorunuzun cevabı önemsiz bir "geçici dosya kullan" veya tüm dosyayı hafızaya yükleyecek bir dehşettir. " Herhangi bir yerde saklamadan eski verileri bir akıştan nasıl alabilirim ?" iyi bir cevabı olamaz.
mosvy

1
@mosvy Eklemek istiyorsanız bu kabul edilebilir bir cevaptır.
Jonah

2
@mosvy Jonah'ın dediği gibi, cevaplar cevap kutusuna gönderilmelidir. Şu anda herhangi bir web sitesini okumanın zor olduğunu biliyorum, ancak lütfen yavaş yavaş görüşünüze damlayan kırmızıyı görmezden gelin ve daha düşük metin alanını kullanın.
wizzwizz4

Yanıtlar:


7

Deneyin:

awk '{x=x $0 ORS}; END{printf "%s", $0 ORS x}'

Misal

Girdimizle bir değişken tanımlayın:

$ input="line 1
> line 2
> line 3"

Komutumuzu çalıştırın:

$ echo "$input" | awk '{x=x $0 ORS}; END{printf "%s", $0 ORS x}'
line 3
line 1
line 2
line 3

Alternatif olarak, elbette, burada-doc kullanabiliriz:

$ cat <<EOS | awk '{x=x $0 ORS}; END{printf "%s", $0 ORS x}'
line 1
line 2
line 3
EOS
line 3
line 1
line 2
line 3

Nasıl çalışır

  • x=x $0 ORS

    Bu, her girdi satırını değişkene ekler x.

    Awk ORSolarak, çıktı kayıt ayırıcıdır . Varsayılan olarak, yeni satır karakteri.

  • END{printf "%s", $0 ORS x}

    Tüm dosyayı okuduktan sonra, son satırı $0ve ardından tüm dosyanın içeriğini yazdırır x.

Bu, belleğe tüm girişi okuduğundan, büyük ( örn. Gigabayt) girişler için uygun olmaz .


Teşekkürler John. Peki bunu OP'deki adlandırılmış dosya örneğime benzer bir şekilde yapmak mümkün değil mi? Stdinin bir şekilde çoğaltıldığını hayal ediyordum ... bir şekilde teeböyle, ama bir stdin ve bir dosya için, aynı stdin'i iki farklı işlem ikamesi haline getirecektik. ya da kabaca buna denk bir şey?
Jonah

5

Eğer stdin aranabilir bir dosyaya işaret ediyorsa (burada bash (ancak diğer tüm kabuklarda değil) burada geçici dosyalarla uygulanan belgeler), kuyruğu alabilir ve ardından tüm içeriği okumadan önce geri arama yapabilirsiniz:

arama işleçleri tcl / perl / python gibi zshveya ksh93kabuklarında veya komut dillerinde kullanılabilir, ancak kullanılamaz bash. Ancak bash, kullanmanız gerektiğinde bu daha gelişmiş çevirmenleri her zaman arayabilirsiniz bash.

ksh93 -c 'tail -n1; cat <#((0))' <<...

Veya

zsh -c 'zmodload zsh/system; tail -n1; sysseek 0; cat' <<...

Şimdi, stdin bir boru veya soket gibi aranamayan bir dosyaya işaret ettiğinde bu işe yaramaz. Daha sonra, tek seçenek tüm girişi okumak ve saklamaktır (bellekte veya geçici bir dosyada ...).

Bellekte saklamak için bazı çözümler zaten verilmiştir.

Tempfile ile aşağıdakileri zshyapabilirsiniz:

seq 10 | zsh -c '{ cat =(sed \$w/dev/fd/3); } 3>&1'

Linux üzerinde, birlikte varsa bashveya zshburada-belgeler için kullandığı geçici dosyaları, aslında çıktıyı depolamak için buraya-belgeye tarafından oluşturulan geçici dosya kullanabileceği herhangi kabuk veya:

seq 10 | {
  chmod u+w /dev/fd/3 # only needed in bash5+
  cat > /dev/fd/3
  tail -n1 /dev/fd/3
  cat <&3
} 3<<EOF
EOF

4
cat <<EOS | sed -ne '1{h;d;}' -e 'H;${G;p;}'
line 1
line 2
line 3
EOS

Bir şeye bu tercüme ile sorunu kullanımları olduğu tailolmasıdır tailihtiyaçları, sonunu bulmak için tüm dosyayı okumak için. Bunu boru hattınızda kullanmak için,

  1. Adlı dokümanın tüm içeriğini sağlayın tail.
  2. Bunu sağlamak tekrar etmek cat.
  3. Bu sırayla.

Zor bit, belgenin içeriğini çoğaltmak değil ( teebunu yapar), ancak tailara geçici bir dosya kullanmadan, belgenin geri kalanının çıktısı alınmadan çıktısının gerçekleşmesini sağlamaktır.

Kullanılması sed(ya awkolduğu gibi, John1024 yapar ) bellekteki verileri depolayarak veri ve sipariş sorununun çifte ayrıştırma kurtulmak alır.

sedBen önermesi çözüm etmektir

  1. 1{h;d;}, ilk satırı olduğu gibi muhafaza alanında saklayın ve sonraki satıra atlayın.
  2. H, gömülü bir yeni satırla birbirini bekletme alanına ekleyin.
  3. ${G;p;}, tutma alanını katıştırılmış yeni satırla son satıra ekleyin ve elde edilen verileri yazdırın.

Bu içine John1024 çözeltisinin oldukça değişmez çevirisidir sedPOSIX standardı sadece tutma alanı KiB diye de 8192 bayt (8 olduğunu garanti eder uyarı ile, fakat bu önerir , gerektiği gibi, bu tampon dinamik olarak tahsis edilmiş ve genişletilir bunların her ikisi de GNU'yu sedve BSD sedyapıyor).


Adlandırılmış bir kanal kullanmanıza izin verirseniz:

mkfifo mypipe
cat <<EOS | tee mypipe | cat <( tail -n 1 mypipe ) -
line 1
line 2
line 3
EOS
rm -f mypipe

Bu tee, verileri aşağı mypipeve aynı zamanda göndermek için kullanır cat. catProgramı ilk kez çıktı okuyacaktır tail(okur mypipe, teeyazıyor) ve sonra doğrudan gelen belgenin kopyasını ekleyin tee.

Bununla birlikte, belgede çok büyükse (borunun tampon boyutundan daha büyük), (isimsiz) borunun boşalmasını beklerken teeyazıyor mypipeve catengelliyor. Ondan okunana kadar boşaltılamaz cat. catbitene kadar tailondan okumazdı . Ve tailbitene kadar teebitirmezdi. Bu klasik bir kilitlenme durumudur.

Varyasyon

tee >( tail -n 1 >mypipe ) | cat mypipe -

aynı sorunu yaşıyor.


2
sedGiriş sadece bir satır (belki varsa bir işe yaramaz sed '1h;1!H;$!d;G'). Ayrıca, bazı seduygulamaların desen ve tutma alanı boyutlarında düşük bir limite sahip olduğuna dikkat edin .
Stéphane Chazelas

Adı verilen boru çözümü aradığım şey. Sınırlama bir utançtır. “Ve tee bitene kadar kuyruk bitmeyecek” dışında açıklamanızı anladım - durumun nedenini açıklayabilir misiniz?
Jonah

2

peeGenellikle "moreutils" adıyla paketlenmiş bir komut satırı yardımcı programları koleksiyonunda adlandırılmış bir araç vardır (veya kendi web sitesinden başka bir şekilde alınabilir ).

Sisteminizde varsa, örneğin örneği için eşdeğer olacaktır:

cat <<EOS | pee 'tail -1' cat 
line 1
line 2
line 3
EOS

İletilen komutların sıralaması peeönemlidir çünkü verilen sırayla yürütülürler.


1

Deneyin:

cat <<EOS # | what goes here now? Nothing!
line 3
line 1
line 2
line 3
EOS

Her şey değişmez veri (bir "burada-belge") ve bu ve istenen çıktı arasındaki fark önemsiz olduğundan, sadece bu literal verileri çıktıyla eşleşecek şekilde masaj yapın.

Şimdi varsayalım ki bir line 3yerden geliyor ve şu değişkente saklanıyor lastline:

cat <<EOS # | what goes here now? Nothing!
$lastline
line 1
line 2
$lastline
EOS

Bu dokümanda değişkenleri değiştirerek metin üretebiliriz. Sadece bu değil, aynı zamanda komut ikamesi kullanarak metin hesaplayabiliriz:

cat <<EOS
this is template text
here we have a hex conversion: $(printf "%x" 42)
EOS

Birden fazla satırı enterpole edebiliriz:

cat <<EOS
multi line
preamble
$(for x in 3 1 2 3; do echo line $x ; done)
epilog
EOS

Genel olarak, burada doc şablonu metin işleme kaçının; enterpolasyonlu kod kullanarak oluşturmaya çalışın.


1
Dürüst olmak gerekirse bunun bir şaka olup olmadığını söyleyemem. cat <<EOS...OP sadece sonrası özgü ve net soru yapmak için "keyfi bir dosyanın catting" için örnek duruyorum oldu. Bu sizin için gerçekten açık değil miydi, yoksa sadece soruyu tam anlamıyla yorumlamanın akıllıca olacağını düşündünüz mü?
Jonah

@Jonah Soru açıkça "[l] et, gerçek bir dosya olmak yerine girdi kaynağımızın stdin olduğunu söylüyor" diyor. "Rasgele dosyalar" hakkında hiçbir şey yok; işte burada dokümanlar. Burada doktor keyfi değildir. Programınıza bir girdi değil, programcının seçtiği sözdiziminin bir parçası.
Kaz

1
Bence bağlam ve mevcut cevaplar durumun bu olduğunu açıkça ortaya koydu, eğer sadece yorumunuzun doğru olması için kelimenin tam anlamıyla ne ben ne de yanıtlayan diğer posterlerin hiçbirinin kod satırı. Yine de, soruyu açık hale getirmek için düzenleyeceğim.
Jonah

1
Kaz, cevabınız için teşekkür ederim, ancak düzenlemenizde bile, sorunun amacını kaçırdığınıza dikkat edin. Bir boru yoluyla rasgele çok satırlı giriş alıyorsunuz . Ne olacağına dair hiçbir fikrin yok. Göreviniz en son girdi satırını ve ardından tüm girdiyi çıktılamaktır.
Jonah

1
Kaz, girdi sadece örnek olarak orada. Ben de dahil olmak üzere çoğu insan, yalnızca soyut sorudan ziyade gerçek girdi ve beklenen çıktı örneğine sahip olmayı yararlı bulmaktadır. Bununla karıştırılan tek kişi sensin.
Jonah

0

Eğer sipariş umurumda değil. O zaman bu işe yarayacak cat lines | tee >(tail -1). Diğerleri söylediler. İstediğiniz sırayla yapmak için dosyayı iki kez okumalısınız veya tüm dosyayı arabelleğe almalısınız.

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.