Перемістіть перші N рядків виводу до кінця, не використовуючи тимчасовий файл


11

Уявіть собі результат такої команди, як

44444
55555
11111
22222
33333

як я можу витягнути перші N рядків (перші два у наведеному вище прикладі) та додати їх наприкінці, але не використовуючи тимчасовий файл (так тільки за допомогою труб)?

11111
22222
33333
44444
55555

Щось уздовж рядків | sed -n '3,5p;1,2p'(що, очевидно, не працює, оскільки sed не дбає про порядок команд).


2
Чому ми не можемо використовувати тимчасовий файл?
Брайям

Відповіді:


13

Просто скопіюйте ці рядки в буфер утримування (потім видаліть їх), а коли в останньому рядку додайте вміст буфера утримування до простору шаблону:

some command | sed '1,NUMBER{           # in this range
H                                       # append line to hold space and
1h                                      # overwrite if it's the 1st line
d                                       # then delete the line
}
$G'                                     # on last line append hold buffer content

З ним gnu sedможна було написати як

some command | sed '1,NUMBER{H;1h;d;};$G'

Ось ще один спосіб з ol ' ed(він rвиводить висновок some commandу текстовий буфер, а потім mвиводить рядки 1,NUMBERпісля останніх $):

ed -s <<IN
r ! some command
1,NUMBERm$
,p
q
IN

Зауважте, що - як вказувалося - вони будуть невдалими, якщо вихід має менше NUMBER+1 рядка. Більш твердим був би підхід ( gnu sedсинтаксис):

some command | sed '1,NUMBER{H;1h;$!d;${g;q;};};$G'

цей лише видаляє рядки в цьому діапазоні, якщо вони не є останнім рядком ( $!d) - інакше він переписує простір шаблону із вмістом буфера утримування ( g), а потім використовує q(після друку поточного простору шаблону).


2
sed -e '1,NUMBER{H;1h;d;}' -e '$G'також працює портативно (зауважте, що деякі sedреалізації не можуть містити більше кількох кілобайт у просторі утримування, тому НОМЕР не може бути занадто великим там)
Stéphane Chazelas

@ StéphaneChazelas - спасибі за вхід - я зазвичай ходжу з однієї команди в кожному рядку , як я знаю , що це портативний - синтаксис кілька виразів завжди був трохи заплутаним для мене , наприклад , в POSIX документи говорять , «The <праву дужку> має бути передує <newline> ", так, згідно з ними, чи не повинно бути цього sed -e '1,NUMBER{H;1h;d' -e'}' -e '$G'?
don_crissti

4
Зазвичай новий -eзамінює новий рядок. d;}ще не POSIX, але портативний. Це буде виправлено у наступній специфікації POSIX. Дивіться austingroupbugs.net/view.php?id=944#c2720
Stéphane Chazelas

2
@don_crissti дякую! було б здорово, якби ви могли також включити коротке пояснення, чому це працює. (Звичайно, я піду шукати, але це дозволить отримати більш цінну відповідь.)
Петро Ухнак

У моїй голові непересічна річ 1,NUMBER{H;1h;d;}- відсутність крапки з комою відразу після вступного дужка. Це, можливо, просто помилка в SunOS 4.1 sed, вирішення якої все ще провідна в мої пальці через 20 років.
zwol

11

awkпідхід:

cmd | awk -v n=3 '
  NR <= n {head = head $0 "\n"; next}
  {print}
  END {printf "%s", head}'

Одна перевага від sedпідходу @ don_crissti полягає в тому, що він все ще працює (виводить рядки), якщо у висновку є nрядки або менше.


Ви можете замінити жорсткий код \nна ORS, щоб це працювало з іншими роздільниками записів (скажімо, ви хочете використовувати абзаци тощо).
fedorqui

6

У мене є, xclipі з цим це можна зробити так:

./a_command | xclip -in && xclip -o | tail -n +3 && xclip -o | head -n 2

Ось його опис:

xclip - command line interface to X selections (clipboard)

NAME
       xclip - command line interface to X selections (clipboard)

SYNOPSIS
       xclip [OPTION] [FILE]...

DESCRIPTION
       Reads from standard in, or from one or more files, and makes the data available as an X selection for pasting into X applications. Prints current X selection to standard out.

       -i, -in
              read text into X selection from standard input or files (default)

       -o, -out
              prints the selection to standard out (generally for piping to a file or program)

3
+1 для творчого (неправильного) використання xclip. Для відповіді потрібен доступний / запущений X-сервер.
jofel

3

Шлях Perl:

perl -ne '$.<3?($f.=$_):print;}{print $f'

Або те саме, що написано менш криптично:

perl -ne 'if($.<3){ $f.=$_ } else{ print } END{print $f}'

Наприклад:

$ cat file
44444
55555
11111
22222
33333

$ cat file | perl -ne '$.<3?($f.=$_):print;}{print $f'
11111
22222
33333
44444
55555

Пояснення

  • -ne: читайте вхідний файл / потік рядок за рядком та застосовуйте сценарій, заданий -eкожним рядком.
  • $.<3: $.- номер поточного рядка, тому змініть 3на кількість рядків, які ви хочете змінити.
  • $.<3?($f.=$_):print;: це умовний оператор, загальний формат - condition ? case1 : case2він запускається, case1якщо conditionє істинним, а case2якщо неправдивим. Тут, якщо номер поточного рядка менше 3, він додає поточний рядок ( $_) до змінної $fі, якщо номер рядка більше 3, він друкує.
  • }{ print $f: the }{perl стенограма для END{}. Він запуститься після того, як всі вхідні рядки будуть оброблені. На цьому етапі ми зібрали всі рядки, які ми хочемо змінити, і надрукуємо всі ті, які ми хочемо залишити в спокої, тому надрукуємо збережені рядки як $f.

1
стосовно версії для гольфу можна видалити декілька символівperl -ne '$.<3?$f.=$_:print}{print $f
123

1

Використовуйте POSIX ex. Так, він призначений для редагування файлів, але він буде працювати в конвеєрі.

printf %s\\n 111 222 333 444 555 | ex -sc '1,2m$|%p|q!' /dev/stdin

Це може мати будь-які довільні команди, додані на початку або в кінці трубопроводу, і вони будуть працювати однаково. А ще краще, враховуючи наявність /dev/stdin, він сумісний з POSIX.

(Я не знаю /dev/stdin, вказано це в POSIX чи ні, але я бачу, що він присутній і в Linux, і в Mac OS X.)

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

Примітка: exКоманда, подана вище, не вдасться, якщо введено менше 2 рядків як вхід.

Подальше читання:


0

Короткий pythonфрагмент:

#!/usr/bin/env python3
import sys
file_ = sys.argv[1]
lines = int(sys.argv[2])
with open(file_) as f:
    f = f.readlines()
    out = f[lines:] + f[:lines]
    print(''.join(out), end='')

Передайте ім'я файлу як перший аргумент та кількість рядків, щоб переміститися як другий аргумент.

Приклад:

$ cat input.txt
44444
55555
11111
22222
33333

$ ./sw_lines.py input.txt 2
11111
22222
33333
44444
55555

$ ./sw_lines.py input.txt 4
33333
44444
55555
11111
22222

0

Якщо ви можете зберегти весь вихід у пам'яті:

data=$(some command)
n=42                  # or whatever
{ tail -n +$((n+1)) <<<"$data"; head -n $n <<<"$data"; } > outputfile

0

Ось ще один варіант, який вимагає GNU sed:

(x=$(gsed -u 3q);cat;printf %s\\n "$x")

-uробить GNU sedнеблокованим, щоб sedкоманда не споживала більше 3 рядків STDIN. Заміна команди видаляє порожні рядки, так що команда не включає порожні рядки в кінці виводу, якщо третій, третій і другий, або третій, другий і перший рядки порожні.

Ви також можете зробити щось подібне:

tee >(sponge /dev/stdout|sed -u 3q)|sed 1,3d

Без sponge /dev/stdout|команди не вдалося б з довгими введеннями. sponge /dev/stdoutможна також замінити tac|tac, хоча це друкує, наприклад, a\ncbякщо вхід знаходиться a\nb\ncтам, де \nпередається лінія, або з (x=$(cat);printf %s\\n "$x"), хоча це видаляє порожні рядки з кінця введення. Команда вище видаляє перший рядок, коли кількість рядків вводу дорівнює одному чи двом.

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