Сценарии Bash и большие файлы (ошибка): вход с чтением, встроенным от перенаправления, дает неожиданный результат

Программа, вспомогательная это man и git diff вызовите назван пейджером. В современных системах называют пейджер по умолчанию less. Несколько десятилетий назад первый пейджер был more, так называемый, потому что это отобразило одну страницу затем, ожидал Вас для нажатия клавиши для наблюдения “больше”. Затем прибыл less, которые также позволяют Вам возвратиться (для наблюдения меньше, так сказать), подтверждая высказывание, что “меньше - больше” (чем больше).

git diff определенное исключение; diff утилита, cvs diff подкоманда, svn diff подуправляйте и так далее просто делают их задание вычислений разности и распечатывания это. Если Вы хотите пролистать разность, необходимо вызвать пейджер явно:

diff file.old file.new | less

Если Вы хотите использовать другой пейджер для man, git diff и другие команды, которые называют пейджер, можно установить PAGER переменная среды, например, путем помещения этого в файл ~/.profile:

export PAGER=most

Вы не можете сделать команды таким как diff вызовите пейджер автоматически. Можно сделать функцию обертки хотя (чтобы быть помещенным в файл инициализации оболочки, например. ~/.zshrc для zsh или ~/.bashrc для удара):

diff () {
  if [ -t 1]; then             # If standard output is a terminal
    command "$@" | less        #    then pipe through less
  else                         # else
    command "$@"               #     run the command (and return its exit status)
  fi
}

Предупреждение: такая функция может иногда быть разрушительной. Можно также сделать псевдоним с одним символом для пейджера, например.

alias p=less P=less

таким образом, можно ввести diff … |p или diff … |P когда Вы хотите пролистать вывод команды.

16
07.05.2014, 22:33
3 ответа

Вы нашли ошибку в ударе, своего рода. Это - известная ошибка с известной фиксацией.

Программы представляют смещение в файле как переменная в некотором целом типе с конечным размером. В былые времена все использовали int для примерно всего, и int тип был ограничен 32 битами, включая знаковый бит, таким образом, он мог сохранить значения от-2147483648 до 2147483647. В наше время существуют названия другого типа разных вещей, включая off_t для смещения в файле.

По умолчанию, off_t 32-разрядный тип на 32-разрядной платформе (позволяющий до 2 ГБ) и 64-разрядный тип на 64-разрядной платформе (позволяющий до 8EB). Однако распространено скомпилировать программы с опцией LARGEFILE, которая переключает тип off_t к 64 бита шириной быть и выполняет вызов программы подходящие реализации функций такой как lseek.

Кажется, что Ваш выполняют удар на 32-разрядной платформе, и Ваш двоичный файл удара не компилируется с большой поддержкой файла. Теперь, когда Вы читаете строку из регулярного файла, колотите, использует внутренний буфер для чтения символов в пакетах для производительности (для получения дополнительной информации, посмотрите источник в builtins/read.def). Когда строка завершена, вызовы удара lseek перематывать файловое смещение назад к позиции конца строки, в случае, если некоторая другая программа, о которой заботятся о положении в том файле. Вызов к lseek происходит в zsyncfc функция в lib/sh/zread.c.

Я не считал источник в большом количестве деталей, но я предполагаю, что чего-то не происходит гладко при переходе, когда абсолютное смещение отрицательно. Таким образом, удар заканчивает тем, что читал при неправильных смещениях, когда он снова наполняет свой буфер, после того, как он передал метку на 2 ГБ.

Если мое заключение является неправильным, и Ваш удар на самом деле работает на 64-разрядной платформе или скомпилированный с поддержкой largefile, которая является определенно ошибкой. Сообщите об этом своему распределению или в восходящем направлении.

Оболочка не является правильным инструментом для обработки таких больших файлов так или иначе. Это будет медленным. Используйте sed, если это возможно, иначе awk.

13
27.01.2020, 19:48
  • 1
    Мерси Gilles. Большой ответ: завершенный, с достаточной информацией для понимания проблемы даже людям без сильного фона CS (32 бита...). (larsks также помогают в опросе на номере строки, и он должен быть подтвержден.) После этого, я также, хотя из проблемы на 32 бита и загрузки источник, но не был к еще этому уровню анализа. Мерси вызов на бис, и бонна journée. –  jfg956 02.03.2012, 10:05

Я не знаю о несправедливости, но это является, конечно, замысловатым. Если Ваши входные строки похожи на это:

YYYY-MM-DD some text ...

Затем нет действительно никакой причины этого:

new_file=${line:0:4}-${line:5:2}-${line:8:2}_file.log

Вы делаете большую работу подстроки для окончания с чем-то, что смотрит... точно способ, которым это уже смотрит в файле. Как насчет этого?

while read line; do
  new_file="${line:0:10}_file.log"
  echo "$line" >> $new_file
done

Это просто захватывает первые 10 символов от строки. Вы могли также обойтись без bash полностью и просто используйте awk:

awk '{print > ($1 "_file.log")}' < file.log

Это захватывает дату в $1 (первый разграниченный пробелом столбец в каждой строке) и использование это для генерации имени файла.

Обратите внимание, что возможно, что существуют некоторые поддельные строки журнала в Ваших файлах. Таким образом, проблема может быть с входом, не Вашим сценарием. Вы могли расшириться awk сценарий для установки флага поддельных строк как это:

awk '
$1 ~ /[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/ {
    print > ($1 "_file.log")
    next
}

{
    print "INVALID:", $0
}
'

Это пишет соответствие строк YYYY-MM-DD к Вашим файлам журнала и строкам флагов, которые не запускаются с метки времени на stdout.

4
27.01.2020, 19:48
  • 1
    Никакие поддельные строки в моем файле: cut -c 1-10 file.log | uniq -c дает мне ожидаемый результат. Я использую ${line:0:4}-${line:5:2}-${line:8:2} потому что я помещу файл в каталог ${line:0:4}/${line:5:2}/${line:8:2}, и я упростил проблему (я обновлю проблемный оператор). Я знаю awk может помочь мне здесь, но я работал в других проблемах с помощью него. То, что я хочу, понимают проблему с bash, не находят альтернативные решения. –  jfg956 01.03.2012, 22:54
  • 2
    Как Вы сказали..., если Вы "упрощаете" проблему в вопросе, Вы, вероятно, не собираетесь получать ответы, которые Вы хотите. Я все еще думаю, что, решая это с ударом не действительно правильный способ обработать этот вид данных, но нет никакой причины, это не должно работать. –  larsks 01.03.2012, 22:56
  • 3
    Упрощенная проблема дает неожиданный результат, который я представил в вопросе, таким образом, я не думаю, что это - упрощение. Кроме того, упрощенная проблема дает подобный результат как cut оператор, который работает. Поскольку я хочу сравнить яблоки с яблоками, не с апельсинами, я должен сделать вещи максимально подобными. –  jfg956 01.03.2012, 23:00
  • 4
    я оставил Вас вопросом, который мог бы помочь выяснить, где вещи спутываются... номер –  larsks 01.03.2012, 23:20

Походит на то, что Вы хотите сделать:

awk '
{  filename = substr($0, 0, 10) "_file.log";  # input format same as output format
   if (filename != lastfile) {
       close(lastfile);
       print 'finished writing to', lastfile;
   }
   print >> filename;
   lastfile=filename;
}' file.log

close мешает открытой таблице файлов заполняться.

2
27.01.2020, 19:48
  • 1
    Спасибо за awk решение. Я уже иду с чем-то подобным. Мой вопрос состоял в том, чтобы понять ограничение удара, для не нахождения альтернативного решения. –  jfg956 01.03.2012, 23:05

Теги

Похожие вопросы