Farklı satır kümelerini yakalamak ve aynı dosyaya kaydetmek için baş ve kuyruk kullanma


10

Yani bu ödev için, ama özel ödev sorusunu sormayacağım.

Bir dosyadan farklı satır kümelerini yakalamak için baş ve kuyruk kullanmam gerekiyor. 6-11 satırları ve 19-24 satırları gibi ikisini de başka bir dosyaya kaydedin. Bunu eki kullanarak yapabilirim biliyorum

head -11 file|tail -6 > file1; head -24 file| tail -6 >> file1. 

Ama sanmamalıyız.
Baş ve kuyruk komutlarını birleştirip dosyaya kaydedebilmemin belirli bir yolu var mı?


1
Onlar özellikle kullanılacağını soruyorsunuz headve tail? Eğer öyleyse, çözümünüz yapabileceğiniz en iyi çözümdür. Başka programları kullanma izniniz varsa sedveya awkdaha iyi çözümlere izin verebilirseniz (yani daha az işlem çağrısı ile).
n.st

Evet, bizden baş ve kuyruk kullanmamızı istiyorlar. Cevabınız için teşekkür ederim.
user2709291

Ben ekleyebilir Bir şey daha: Sen (ekleme çıkış yönlendirme sorunun üstesinden gelebilirsiniz >>onların birleştirilmiş çıktısını yönlendirmek için parantez içinde iki komutları içine alarak): (head -11 file | tail -6; head -24 file | tail -6) > file1. Gerçekten güzel olan kişisel tercih geliyor.
n.st

Çok iyi çalışacak teşekkürler. Gerçekten onu takdir ederim.
user2709291

Yanıtlar:


11

headKomutları { ... ; }benzer bir yapı kullanarak grupluyorsanız, bunu tek başına ve temel aritmetik işlemlerle yapabilirsiniz

{ head -n ...; head -n ...; ...; } < input_file > output_file

burada tüm komutlar aynı girişi paylaşır (teşekkürler @mikeserv ).
6-11 ve 19-24 arasındaki satırları elde etmek:

head -n 5 >/dev/null  # dump the first 5 lines to `/dev/null` then
head -n 6             # print the next 6 lines (i.e. from 6 to 11) then
head -n 7 >/dev/null  # dump the next 7 lines to `/dev/null` ( from 12 to 18)
head -n 6             # then print the next 6 lines (19 up to 24)

Yani, temelde, şöyle koştunuz:

{ head -n 5 >/dev/null; head -n 6; head -n 7 >/dev/null; head -n 6; } < input_file > output_file

Benim için çalışmıyor. Giriş ilk kafa tarafından kolayca tüketilir
Whimusical

6

Yeniden { … }yönlendirme işlecini bir bileşik komutuna uygulamak için gruplandırma yapısını kullanabilirsiniz .

{ head -n 11 file | tail -n 6; head -n 24 file | tail -n 6; } >file1

İlk M + N satırlarını çoğaltmak ve yalnızca son N'yi tutmak yerine, ilk M satırlarını atlayabilir ve sonraki N'yi çoğaltabilirsiniz. Bu, büyük dosyalarda önemli ölçüde daha hızlıdır . Sakının +Nargümanı tailatlamak hatlarının sayısı, ama bir değil artı olduğunu - 1'den numaralı hatları ile yazdırmak için ilk satırın numarası.

{ tail -n +6 file | head -n 6; tail -n +19 file | head -n 6; } >file1

Her iki durumda da, çıktı dosyası yalnızca bir kez açılır, ancak her snippet'in çıkarması için girdi dosyası bir kez geçirilir. Girdileri gruplamaya ne dersiniz?

{ tail -n +6 | head -n 6; tail -n +14 | head -n 6; } <file >file1

Genel olarak, bu işe yaramaz. (Bazı sistemlerde, en azından girdi normal bir dosya olduğunda işe yarayabilir.) Neden? Giriş tamponlaması nedeniyle . Çoğu program, tailgirdi bayt byte baytını okumaz, ancak her seferinde birkaç kilobayt okur, çünkü daha hızlıdır. Bu yüzden tailbirkaç kilobayt okur, başlangıçta biraz atlar, biraz daha fazla geçer headve durur - ancak okunan şey okunur ve bir sonraki komut için kullanılamaz.

Başka bir yaklaşım , hatları atlamak için headborulu kullanmaktır /dev/null.

{ head -n 5 >/dev/null; head -n 6; head -n 7 >/dev/null; head -n 6; } <file >file1

Yine, tamponlama nedeniyle çalışacağı garanti edilmez. headGiriş normal bir dosyadan olduğunda GNU coreutils (gömülü olmayan Linux sistemlerinde bulunan komut) komutuyla çalışır . Çünkü bu uygulama headistediklerini okuduğunda , dosya konumunu çıktı vermediği ilk bayta ayarlar . Giriş bir boru ise bu çalışmaz.

Bir dosyadan birkaç satır dizisini yazdırmanın daha basit bir yolu, sed veya awk gibi daha genel bir araç çağırmaktır . (Bu daha yavaş olabilir, ancak yalnızca çok büyük dosyalar için önemlidir.)

sed -n -e '6,11p' -e '19,24p' <file >file1
sed -e '1,5d' -e '12,18d' -e '24q' <file >file1
awk '6<=NR && NR<=11 || 19<=NR && NR<=24' <file >file1
awk 'NR==6, NR==11; NR==19, NR==24' <file >file1

2
O değil , işin başına bir borudur olsa kesinlikle dediğin gibi - bu standart, belirtilen davranıştır değil paylaşılan girişi için güvenilir bir giriş kaynağı. YARDIMCI TANIM VARSAYILANLAR : Standart bir yardımcı program aranabilir bir girdi dosyasını okuduğunda ve dosya sonuna ulaşmadan hatasız olarak sonlandırıldığında, yardımcı program açık dosya açıklamasındaki dosya ofsetinin, tarafından işlenen son baytın hemen ötesinde düzgün bir şekilde konumlandırılmasını sağlar Yardımcı program.
07:18

2

Baş ve kuyruk kullanmanız gerektiğini söylediğinizi biliyorum, ama sed kesinlikle bu iş için daha basit bir araç.

$ cat foo
a 1 1
a 2 1
b 1 1
a 3 1
c 3 1
c 3 1
$ sed -ne '2,4p;6p' foo
a 2 1
b 1 1
a 3 1
c 3 1

Hatta blokları başka bir işlemle bir dizede oluşturabilir ve bunu sed ile çalıştırabilirsiniz.

$ a="2,4p;6p"
$ sed -ne $a foo
a 2 1
b 1 1
a 3 1
c 3 1

-n çıktıyı reddeder, ardından p ile yazdırılacak aralıkları belirtirsiniz; aralığın ilk ve son sayısı virgülle ayrılır.

Bununla birlikte, @don_crissti'nin önerdiği komut gruplamasını yapabilir veya dosyaya birkaç kez döngü yaparak baş / kuyruk her geçişinizde bir yığın satır yakalayabilirsiniz.

$ head -4 foo | tail -3; head -6 foo | tail -1
a 2 1
b 1 1
a 3 1
c 3 1

Bir dosyada ne kadar çok satır ve ne kadar blok varsa, o kadar verimli sed elde edilir.


2

Sizinle sedşunları yapabilirsiniz:

sed '24q;1,5d;12,18d' <infile >outfile

... Muhtemelen daha etkili bir çözüm elde edilebilirdi head. Don zaten bunun nasıl iyi çalışabileceğini gösterdi, ama ben de onunla oynuyorum. Bu özel durumu ele almak için yapabileceğiniz bir şey:

for   n in 5 6 7 6
do    head -n"$n" >&"$((1+n%2))"
done  <infile >outfile 2>/dev/null

... yinelemenin değerinin çift ​​ya da tek bir sayı olup olmadığına bağlı olarak headya outfileda /dev/nullbu $nsayıya 4 kez yazmayı gerektirir .

Daha genel durumlar için, bunu zaten sahip olduğum diğer şeylerden birlikte topladım:

somehead()( 
### call it like:
### somehead -[repeat] [-][numlines]* <infile >outfile
    set -e -- "${1#-}" "$@"                             #-e for arg validation
    r=; cd -- "${TMP:-/tmp}"                            #go to tmp
    dd bs=4096 of="$$$$" <&4 2>&3 &                     #dd <in >tmpfile &bg
    until [ -s "$$$$" ]; do :; done                     #wait while tmpfile empty
    exec <"$$$$" 4<&-;   rm "$$$$"                      #<tmpfile; rm tmpfile
    [ "$3${1}0" -ne "$3${2#?}0" ]          ||           #validate args - chk $1
            shift "$(((r=-${1:--1})||1))"; shift        #shift 1||2
    while [ "$(((r+=(_n=1))-1))" -ne 0 ]   &&           #while ! $rptmax &&
          IFS= read -r l                   &&           #      ! EOF     &&
          printf "%.$(($1>0?${#l}+1:0))s" "$l           #      ? printf  do
";  do    for n do [ "${n#-}" -gt 0 ]      || exit      #args all -[nums>0]
          head "-n$((${n#-}-_n))" >&"$((n>(_n=0)?1:3))" #head -n?$1 >?[+-]
    done; done                                          #done and done
)   4<&0 3>/dev/null                                    #4<for dd 3>for head

Bu sizin gibi bir şey yapabilirsiniz:

 seq 100 | somehead -1 -5 6 -7 6

... hangi baskı ...

6
7
8
9
10
11
19
20
21
22
23
24

İlk argümanının a ile başlayan bir tekrar sayısı -veya başarısız olması durumunda sadece a -. Bir sayım sağlanırsa, aşağıdaki bağımsız değişkenlerde belirtilen çizgi desenini belirtildiği kadar tekrar eder ve bunu yapar yapmaz durur.

Sonraki her bağımsız değişken için, yazılması gereken bir satır sayısını belirtmek için negatif bir tamsayıyı ve yazılması gereken /dev/nullbir satır sayısını belirtmek için pozitif bir tamsayıyı yorumlayacaktır stdout.

Yukarıdaki örnekte, ilk 5 satırı /dev/null, sonraki 6'dan stdoutsonraki sonraki 7'ye /dev/nulltekrar ve sonraki 6'ya tekrar yazdırır stdout. Arglerinin sonuncusuna ulaşıp -1tekrar sayımıyla tamamen döngüye girdikten sonra kapanır. İlk argüman olsaydı -2, süreci bir kez daha ya da -olabildiğince tekrar ederdi .

Her arg çevrimi için whiledöngü bir kez işlenir. Her döngünün üstünde ilk satır stdinkabuk değişkenine okunur $l. while head </dev/null; do :; doneSüresiz olarak tekrarlanacağı için bu gereklidir - headdosya sonuna geldiğinde geri dönüşünü gösterir. Bu nedenle EOF'a karşı kontrol , yalnızca ikinci argüman pozitif bir tamsayı olduğunda adanmış readve yeni bir satır printfyazacaktır .$lstdout

readKontrol döngüsü, bir az şey komplike bir döngü olarak adlandırılır hemen sonra, çünkü - bir forbağımsız değişken yineler döngü 2-$#de temsil edildiği gibi $n, üst her okunması için whiledöngü. Bu, her yinelemede ilk bağımsız değişkenin komut satırında belirtilen değerden biri tarafından azaltılması gerektiği, ancak diğerlerinin orijinal değerlerini koruması gerektiği ve böylece $_nmarker var değerinin değerinin her birinden çıkarılması gerektiği, ancak yalnızca ilk arg için 0'dan büyük bir değer.

Bu, işlevin ana döngüsünü oluşturur, ancak kodun büyük kısmı üsttedir ve işlevin giriş olarak bir boruyu temiz bir şekilde tamponlamasını sağlar. Bu, önce ddbir parçayı 4k boyutta blok boyutlarında çıktıya bir tmp dosyasına kopyalamak için arka planı çağırarak çalışır . Daha sonra işlev, tek bir tam döngüyü bile neredeyse hiç tamamlamaması gereken bir tutma döngüsü oluşturur; yalnızca ddişlevden önce dosyaya en az tek bir yazma işlemi yaptıktan sonra stdin'ini tmpfile ile bağlantılı bir dosya tanımlayıcıyla değiştirmesini sağlar ve daha sonra dosyayı derhalrm. Bu, işlevin tuzak gerektirmeden veya temizleme için başka bir şekilde akışı güvenilir bir şekilde işlemesini sağlar - işlev fd'de iddia ettiği gibi serbest bırakılır yayınlanmaz, yalnızca adlandırılmış dosya sistemi bağlantısı zaten kaldırıldığı için tmpfile varlığını durduracaktır.


0

Bunun gibi bir bash işlevi kullanın:

seq 1 30 > input.txt
f(){ head $1 input.txt | tail $2 >> output.txt ;}; f -11 -2; f -24 -3
cat output.txt
10
11
22
23
24

Bu, bu durumda biraz fazlalıktır, ancak filtreleriniz büyürse bir nimet olabilir.

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.