Рассмотрим этот скрипт:
tmpfile=$(mktemp)
cat <<EOS > "$tmpfile"
line 1
line 2
line 3
EOS
cat <(tail -1 "$tmpfile") "$tmpfile"
Это работает и выводит:
line 3
line 1
line 2
line 3
Допустим, что наш источник ввода, а не фактический файл, был вместо этого stdin:
cat <<EOS | # what goes here now?
line 1
line 2
line 3
EOS
Как мы модифицируем команду:
cat <(tail -1 "$tmpfile") "$tmpfile"
Так что он все еще производит тот же результат, в этом другом контексте?
ПРИМЕЧАНИЕ: конкретный Heredoc, которым я занимаюсь, а также использование самого Heredoc, является просто иллюстративным. Любой приемлемый ответ должен предполагать, что он получает произвольные данные через стандартный ввод .
Ответы:
Пытаться:
пример
Определите переменную с помощью нашего ввода:
Запустите нашу команду:
В качестве альтернативы, конечно, мы могли бы использовать здесь документ:
Как это работает
x=x $0 ORS
Это добавляет каждую строку ввода к переменной
x
.В awk
ORS
- разделитель выходных записей . По умолчанию это символ перевода строки.END{printf "%s", $0 ORS x}
После того как мы прочитали весь файл, будет напечатана последняя строка
$0
, за которой следует содержимое всего файлаx
.Так как при этом считывается весь ввод в память, он не подходит для больших ( например, гигабайтных) вводов.
источник
tee
, но из stdin и файла мы бы передавали один и тот же stdin в две разные подстановки процесса. или что-нибудь, что было бы примерно эквивалентно этому?Если stdin указывает на доступный для поиска файл (как, например, в случае документов bash (но не всех других оболочек), которые реализованы с временными файлами), вы можете получить хвост и затем выполнить обратный поиск перед чтением полного содержимого:
Операторы поиска доступны в оболочках
zsh
илиksh93
, или в языках сценариев, таких как tcl / perl / python, но не вbash
. Но вы всегда можете позвонить этим более продвинутым переводчикам,bash
если вам придется их использоватьbash
.Или
Теперь это не сработает, когда stdin указывает на файлы без возможности поиска, такие как канал или сокет. Тогда единственный вариант - прочитать и сохранить (в памяти или во временном файле ...) весь ввод.
Некоторые решения для хранения в памяти уже были даны.
С помощью временного файла
zsh
вы можете сделать это с помощью:Если на Linux, с
bash
илиzsh
или любой оболочкой , что файлы используют временные для здесь-документов, вы могли бы реально использовать временный файл , созданный с помощью документ-здесь , чтобы сохранить результат:источник
Проблема с переводом этого к чему-то, что использует то,
tail
чтоtail
нужно прочитать весь файл, чтобы найти его конец. Чтобы использовать это в своем конвейере, вам нужноtail
.cat
.Сложность заключается не в том, чтобы дублировать содержимое документа (
tee
делает это), а в том, чтобы получить результатtail
до того, как будет выведен остальной документ, без использования промежуточного временного файла.Использование
sed
(илиawk
, как это делает John1024 ) избавляет от двойного анализа данных и проблемы упорядочения, сохраняя данные в памяти.sed
Решение , которое я предлагаю , чтобы1{h;d;}
, сохраните первую строку в пространстве удержания, как есть, и перейдите к следующей строке.H
, добавьте каждую строку к пробелу со встроенной новой строкой.${G;p;}
, добавьте пробел к последней строке со встроенной новой строкой и напечатайте полученные данные.Это довольно буквальный перевод решения John1024 в
sed
, с оговоркой, что стандарт POSIX гарантирует только то, что пространство удержания составляет по крайней мере 8192 байта (8 КиБ; но он рекомендует, чтобы этот буфер динамически выделялся и расширялся по мере необходимости, что оба GNUsed
и BSDsed
делает).Если вы позволите себе использовать именованный канал:
Это используется
tee
для отправки данных внизmypipe
и в то же времяcat
.cat
Утилита первого чтения выводаtail
(который считывает сmypipe
, которыйtee
пишет в), а затем добавить копию документа , приходящий непосредственно изtee
.Однако в этом есть серьезный недостаток, заключающийся в том, что если документ слишком большой (больше, чем размер буфера канала),
tee
записьmypipe
иcat
блокирование будут выполняться в ожидании опустошения (без имени) канала. Он не будет опустошен, пока неcat
прочитан из него.cat
не будет читать с него, покаtail
не закончил. Иtail
не закончил бы, покаtee
не закончил. Это классическая тупиковая ситуация.Вариация
имеет ту же проблему.
источник
sed
Один не работает , если вход имеет только одну строку (может бытьsed '1h;1!H;$!d;G'
). Также обратите внимание, что несколькоsed
реализаций имеют низкий предел размера своего шаблона и места для хранения.Существует инструмент, названный
pee
в наборе утилит командной строки, обычно упакованный с именем «moreutils» (или иным образом доступный на его домашнем веб-сайте ).Если вы можете иметь его в своей системе, то эквивалент для вашего примера будет выглядеть так:
Порядок выполнения команд
pee
важен, потому что они выполняются в предоставленной последовательности.источник
Пытаться:
Поскольку все это буквальные данные («документ здесь-это»), а разница между ним и желаемым выводом тривиальна, просто помассируйте эти буквальные данные прямо в соответствии с выводом.
Теперь предположим, что он
line 3
откуда-то и хранится в переменной с именемlastline
:В этом документе мы можем генерировать текст, подставляя переменные. Не только это, но мы можем вычислить текст с помощью подстановки команд:
Мы можем интерполировать несколько строк:
В общем, избегайте текстовой обработки здесь шаблона документа; попытаться сгенерировать его, используя интерполированный код.
источник
cat <<EOS...
в OP была просто примером для «кошки произвольного файла», чтобы конкретизировать пост и прояснить вопрос. Было ли это на самом деле неочевидным для вас, или вы просто подумали, что было бы разумно толковать вопрос буквально?Если вы не заботитесь о заказе. Тогда это будет работать
cat lines | tee >(tail -1)
. Как уже говорили другие. Вам нужно прочитать файл дважды или поместить в буфер весь файл, чтобы сделать это в том порядке, в котором вы просили.источник