Використовуючи голову та хвіст, щоб схопити різні набори ліній та зберегти їх у одному файлі


10

Так це для домашніх завдань, але я не буду ставити конкретного домашнього завдання.

Мені потрібно використовувати голову і хвіст, щоб схопити різні набори рядків з одного файлу. Так як рядки 6-11 та рядки 19-24 та збережіть їх обидва в інший файл. Я знаю, що можу це зробити, використовуючи додаток типу

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

Але я не думаю, що ми цього повинні.
Чи є певний спосіб я комбінувати команди голови та хвоста та зберігати у файлі?


1
Вони спеціально просять вас використовувати headі tail? Якщо так, то ваше рішення майже найкраще, що ви можете зробити. Якщо вам дозволено використовувати інші програми, sedабо awkви можете дозволити кращі рішення (наприклад, з меншою кількістю викликів процесу).
n.st

Так, вони просять, щоб ми використовували голову та хвіст. Спасибі за вашу відповідь.
user2709291

Ще одна річ , яку я можу додати: Ви можете отримати навколо додається перенаправлення виведення ( >>) приклавши дві команди в дужках , щоб перенаправити їх каскадний вихід: (head -11 file | tail -6; head -24 file | tail -6) > file1. Це дійсно зводиться до особистих переваг, що приємніше.
n.st

Дякую, що вийде дуже добре. Я дійсно ціную це.
user2709291

Відповіді:


11

Ви можете це робити headокремо і основними арифметичними, якщо ви групуєте команди за { ... ; }допомогою подібної конструкції

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

де всі команди мають однаковий вхід (спасибі @mikeserv ).
Отримання рядків 6-11 та рядків 19-24 еквівалентно:

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)

Отже, ви в основному біжите:

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

Це не працює для мене. Вхід споживається першою головою
примхливим

6

Ви можете використовувати структуру { … }групування, щоб застосувати оператор перенаправлення до складеної команди.

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

Замість того, щоб дублювати перші M + N рядків і зберігати лише останні N, ви можете пропустити перші M рядки і дублювати наступні N. Це помітно швидше у великих файлах . Будьте уважні, що +Nаргумент - tailце не кількість рядків, які потрібно пропустити, а один плюс до цього - це номер першого рядка для друку з рядками, пронумерованими від 1.

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

У будь-якому випадку вихідний файл відкривається лише один раз, але вхідний файл проходить один раз для кожного фрагмента для вилучення. Як щодо групування вхідних даних?

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

Взагалі це не працює. (Це може працювати в деяких системах, принаймні, коли вхід є звичайним файлом.) Чому? Через вхідну буферизацію . Більшість програм, в тому числі tail, не читають вхідний байт за байтом, а кілька кілобайт одночасно, тому що це швидше. Тому tailчитає кілька кілобайт, трохи пропускає на початку, передає трохи більше head, і зупиняється, - але те, що читається, читається, і не доступне для наступної команди.

Інший підхід - використовувати headтрубопровід, /dev/nullщоб пропустити лінії.

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

Знову ж таки, це не гарантовано спрацює через буферизацію. Це трапляється працювати з headкомандою GNU coreutils (тією, яку знайдено в невбудованих системах Linux), коли вхід із звичайного файла. Це тому, що після того, як ця реалізація headпрочитала те, що хоче, вона встановлює позицію файлу на перший байт, який він не виводить. Це не працює, якщо вхід - це труба.

Найпростіший спосіб друкувати кілька послідовностей рядків з файлу - викликати більш загальний інструмент, такий як sed або awk . (Це може бути повільніше, але це має значення лише для надзвичайно великих файлів.)

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
Це не трапляється, це стандартне, визначене поведінка - хоча звичайно, як ви кажете, труба не є надійним джерелом входу для спільного введення. ЗАМЕЧЕННЯ ОПИСУ ПОЛОЖЕННЯ : Коли стандартна утиліта зчитує шуканий вхідний файл і закінчується без помилки до того, як досягне кінця файлу, утиліта забезпечить правильне розміщення зміщення файлу у відкритому описі файлу безпосередньо за останнім байтом, обробленим утиліта.
mikeserv

2

Я знаю, ви сказали, що вам потрібно використовувати голову і хвіст, але sed є, безумовно, простішим інструментом для роботи тут.

$ 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

Ви навіть можете побудувати блоки в рядку за допомогою іншого процесу і запустити його через sed.

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

-n заперечує вихід, тоді ви вказуєте діапазони для друку з p, при цьому перше і останнє число діапазону розділяються комою.

Зважаючи на це, ви можете або виконати групування команд, яку запропонував @don_crissti, або прокрутити файл декілька разів, голова / хвіст хапати шматок рядків кожного разу, коли ви проходите.

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

Чим більше рядків у файлі і чим більше блоків у вас, тим ефективнішим буде sed.


2

З sedвами ви можете:

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

... Можливо, було б більш ефективним рішенням head. Дон уже продемонстрував, як це може працювати дуже добре, але я теж пограв із цим. Щось, що ви можете зробити для вирішення цього конкретного випадку:

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

... що закликає head4 рази записувати або до, outfileабо /dev/nullзалежно від того, чи є цією ітерацією значення $nпарного чи непарного числа.

Для більш загальних випадків я співав це разом із деякими іншими речами:

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

Це можна зробити так, як:

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

... які друкує ...

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

Першим аргументом очікується, що це повторне підрахунок з префіксом a -, або, якщо цього не відбувається, просто a -. Якщо підрахунок передбачений, він повторить шаблон лінії, вказаний у наступних аргументах, стільки разів, скільки зазначено, і зупиниться, як тільки це зробить.

Для кожного аргументу, що випливає, він буде інтерпретувати негативне ціле число, щоб вказати кількість рядків, до якого слід записати, /dev/nullа додатне ціле число, щоб вказати кількість рядків, до яких слід записати stdout.

Отже, у наведеному вище прикладі вона друкує перші 5 рядків до /dev/null, наступні 6 до stdout, наступні 7 /dev/nullзнову і наступні 6 ще раз до stdout. Діставшись до останнього свого аргументу і повністю проїхавшись через -1повторний підрахунок, він потім виходить. Якби це був перший аргумент, -2він би повторив процес ще раз, або -як би довше, ніж міг.

Для кожного циклу аргументу цикл whileобробляється один раз. У верхній частині кожної петлі перший рядок stdinзчитується в змінну оболонки $l. Це необхідно, тому що while head </dev/null; do :; doneбуде повторюватися нескінченно - headвказує при поверненні, коли він досяг кінця файлу. Таким чином, перевірка на EOF присвячена readі printfзапише $lплюс новий рядок stdoutлише у тому випадку, якщо другий аргумент є натуральним числом.

readПеревірка ускладнює петлю трохи , тому що відразу ж після того, як інший цикл називається - forцикл , який перебирає арг , 2-$#як представлено в $nкожній ітерації батьківського whileциклу. Це означає, що для кожної ітерації перший аргумент повинен бути зменшений на одиницю від значення, вказаного в командному рядку, але всі інші повинні зберігати свої початкові значення, і тому значення $_nvar маркера віднімається від кожного, але тільки коли-небудь має a значення більше 0 для першого аргументу

Це становить основний цикл функції, але основна частина коду знаходиться вгорі і призначена для того, щоб функція могла чисто буферувати навіть трубу як вхід. Це працює, спочатку викликаючи фонове зображення, ddщоб скопіювати його в tmpfile на виході з розмірами 4 к. Штуки. Потім функція встановлює цикл утримування - який майже ніколи не повинен завершити навіть єдиний повний цикл - лише для того, щоб переконатися, що ddвін зробив принаймні одне записування у файл до того, як функція потім замінить свій stdin дескриптором файлів, пов'язаним з tmpfile і після цього відразу від’єднується файл ізrm. Це дає можливість функції надійно обробляти потік, не вимагаючи пасток або інакше для очищення - як тільки функція звільнить її претензію на fd, tmpfile перестане існувати, оскільки єдине посилання з файловою системою вже видалено.


0

Використовуйте функцію bash, як це:

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

Це трохи перевитрати в цьому випадку, але якщо ваші фільтри зростають, це може стати вигодою.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.