Як я можу друкувати рядки з файлу назад (без використання "tac")?


26

Я хочу надрукувати рядки з файлу назад, не використовуючи tacкоманди. Чи є якесь інше рішення зробити таку справу з басом?


Відповіді:


33

Використання sedдля емуляції tac:

sed '1!G;h;$!d' ${inputfile}

3
Це хороше рішення, але деякі пояснення, як це працює, будуть ще кращими.
Чен Леві

10
Це відомий sedоднолінійний. Див. "36. Зворотний порядок рядків (емуляція" tac "Команда Unix)." у відомому седлайн-лайнері Пояснено для повного пояснення того, як це працює.
Johnsyweb

6
Можливо, варто відзначити: "Ці два однофайлери фактично використовують багато пам'яті, оскільки вони зберігають весь файл у буфері утримування у зворотному порядку перед його роздрукуванням. Уникайте цих одношарових файлів для великих файлів."
Містер Шикаданс

Так само виконайте всі інші відповіді (крім, можливо, тієї, що використовує sort- є ймовірність, що він використовуватиме тимчасовий файл).
Випадково832

1
Зауважте, що tac швидше для звичайних файлів, оскільки він читає файл назад. Для труб це потрібно робити так само, як і для інших рішень (зберігати в пам'яті або тимчасових файлах), тому це не суттєво швидше.
Стефан Шазелас


6

awk '{a[i++]=$0} END {for (j=i-1; j>=0;) print a[j--] }' file.txt

через awk один лайнер


4
Коротше:awk 'a=$0RS a{}END{printf a}'
ніндзя

@ninjalj: він може бути коротшим, але він стає дуже повільним, оскільки розмір файлу збільшується. Я здався, чекаючи 2 хв 30 сек. but your first perl reverse <> `це найкраща / найшвидша відповідь на сторінці (для мене), в 10 разів швидше, ніж ця awkвідповідь (усі анкрети awk приблизно однакові, часом)
Peter.O

1
Абоawk '{a[NR]=$0} END {while (NR) print a[NR--]}'
Стефан Шазелас

5

Як ви попросили зробити це в bash, ось рішення, яке не використовує awk, sed або perl, а лише функцію bash:

reverse ()
{
    local line
    if IFS= read -r line
    then
        reverse
        printf '%s\n' "$line"
    fi
}

Вихід

echo 'a
b
c
d
' | reverse

є

d
c
b
a

Як і очікувалося.

Але майте на увазі, що рядки зберігаються в пам'яті, по одному рядку в кожному рекурсивно називаному екземплярі функції. Тож обережно з великими файлами.


1
Це швидко стає непрактично повільним, оскільки збільшується розмір файлу, порівняно з навіть найповільнішими з інших відповідей, і, як ви вже запропонували, він видуває пам'ять досить легко: * баш-рекурсивна функція ..., але цікава ідея.
Помилка

4

Ви можете передати це через:

awk '{print NR" "$0}' | sort -k1 -n -r | sed 's/^[^ ]* //g'

У awkпрефікси кожен рядок з номером рядка з подальшим пропуском. sortЗмінює порядок рядків при сортуванні по першому полю (номер рядка) в зворотному порядку, числовий стиль. І sedсмужки відкреслити номери ліній.

Наступний приклад показує це в дії:

pax$ echo 'a
b
c
d
e
f
g
h
i
j
k
l' | awk '{print NR" "$0}' | sort -k1 -n -r | sed 's/^[^ ]* //g'

Він виводить:

l
k
j
i
h
g
f
e
d
c
b
a

Добре. Спасибі! Я спробую це. І дякую за коментарі!

5
cat -nдіє так, якawk '{print NR" "$0}'
Чен Леві

2
Я думаю, що це називається перетворенням Шварца , або прикрасити-сортувати-неприкрасити
ніндзя

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

1

У перл:

cat <somefile> | perl -e 'while(<>) { push @a, $_; } foreach (reverse(@a)) { print; }'

2
або коротшеperl -e 'print reverse<>'
ніндзя

2
( На насправді, є більш короткий, але це дійсно негарно, свідок його жах: perl -pe '$\=$_.$\}{')
ninjalj

@Frederik Deweerdt: Швидко, але він втрачає перший рядок ... @ ninjalj: reverse<)швидко: добре! але "по-справжньому потворний" надзвичайно повільний, оскільки кількість рядків збільшується. !! ....
Peter.O

@ Peter.O там -nбуло зайвим, дякую.
Фредерік Дьюердт

Приємно в цьому - те, що вміст файлу не обов'язково читається в пам'яті (за винятком випадків, коли вони є шматками sort).
Kusalananda

0

Рішення лише для BASH

читати файл у масив bash (один рядок = один елемент масиву) та друкувати масив у зворотному порядку:

i=0 

while read line[$i] ; do
    i=$(($i+1))
done < FILE


for (( i=${#line[@]}-1 ; i>=0 ; i-- )) ; do
    echo ${line[$i]}
done

Спробуйте це з відступними рядками ... версія philfr трохи краща, але все-таки veeeery slooooow так справді, що стосується обробки тексту, ніколи не використовуйте while..read.
don_crissti

Використовуйте IFS=''та read -rне дозволяйте всілякі втечі та вилучення IFS від викручування. Я думаю, що bash mapfile ARRAY_NAMEвбудований є кращим рішенням для читання в масивах.
Arthur2e5

0

Bash, з mapfileзгадуваним у коментарях до fiximan, і насправді можливою кращою версією:

# last [LINES=50]
_last_flush(){ BUF=("${BUF[@]:$(($1-LINES)):$1}"); } # flush the lines, can be slow.
last(){
  local LINES="${1:-10}" BUF
  ((LINES)) || return 2
  mapfile -C _last_flush -c $(( (LINES<5000) ? 5000 : LINES+5 )) BUF
  BUF=("${BUF[@]}") # Make sure the array subscripts make sence, can be slow.
  ((LINES="${#BUF[@]}" > LINES ? LINES : "${#BUF[@]}"))
  for ((i="${#BUF[@]}"; i>"${#BUF[@]}"-LINES; i--)); do echo -n "${BUF[i]}"; done
}

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



0
  • Пронумеруйте рядки з nl
  • сортувати у зворотному порядку за номером
  • вийміть числа за допомогою sed

як показано тут:

echo 'e
> f
> a
> c
> ' | nl | sort -nr | sed -r 's/^ *[0-9]+\t//'

Результат:

c
a
f
e

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