Буферизация stdout отдельно от stderr

Я мог бы подойти к этому с помощью awk таким образом:

  1. установить разделитель полей вывода на то, что является разделителем полей ввода, который мы назначаем как запятую-F,
  2. для каждой строки переназначьте значения полей 1, 5 и 6 как исходные значения, но заключенные в двойные -кавычки. Очевидный беспорядок в кавычках возникает из-за того, что я использовал двойные -кавычки для создания окружающей строки, и поскольку единственная строка, которую я хочу напечатать , — это двойная -кавычка, я должен экранировать ее, поэтому каждый двойная -кавычка, которую я хочу, заканчивается "\"".
  3. Как только поля будут обновлены, напечатайте новую -комбинированную строку.

Скрипт:

awk -F, 'BEGIN{ OFS=FS } {$1="\""$1"\""; $5="\""$5"\""; $6="\""$6"\""; print }' < input.csv > output.csv

Когда вы обнаружите, что вам нужно указать больше полей, просто сделайте то же самое, что и с полями 1, 5 и 6 выше.

4
04.05.2020, 22:03
2 ответа
#!/bin/bash

mycmd() {
  echo progress >&2
  echo out
  sleep 1
  echo progress >&2
  echo out
  sleep 1
  echo progress >&2
  echo out
  sleep 1
}

# Make sure the tempfile is made in a secure way that avoids indeterminism / potential race conditions / etc.
tmpfile="$(tempfile)"
# Write output to the file
mycmd 2>&1 >"$tmpfile"; cat "$tmpfile"; rm "$tmpfile"

# or if output is small: Write it to an environment variable.
# These are below the limit for "small" for different shells
# (Determined by making "mycmd" output that amount of data)
# ash  30 GB
# dash 30 GB
# bash 3 GB
# zsh  3 GB
# ksh  1 GB
out=`mycmd` 2>&1
echo "$out"

Оба решения будут видны пользователю root (в файловой системе и в /proc/ */environ )и никаким другим пользователям в системе.

Если в вашей системе есть spongeи оболочка поддерживает /dev/stdout:

{
  cmd 2>&3 3>&- |
    sponge /dev/stdout 3>&-;
} 3>&1

(Версия Кш:

$ ksh --version
  version         sh (AT&T Research) 93u+ 2012-08-01

)

2
28.04.2021, 23:16

Вы должны сделать что-то вроде:

{
  cmd 2>&3 3>&- |
    awk '    {saved = saved $0 ORS}
         END {printf "%s", saved}' 3>&-
} 3>&1

Здесь используется awkдля хранения всего вывода cmd(после того, как cmdзаписал свой вывод stderr в stdout )скрипта.

awkбудет считываться до тех пор, пока не будет закрыт записывающий конец канала. Обычно это происходит только тогда, когдаcmd(и все процессы, которые он разветвляет и все еще удерживает fd для канала ), завершаются. Если по какой-то причине cmdрешит явным образом закрыть свой стандартный вывод, а затем записать еще какой-то прогресс в stderr, этот дополнительный прогресс может закончиться после нормального вывода. Вы можете обойти это, заменив cmdна (cmd; exit), где awkтакже будет ждать завершения этой подоболочки (, которая также имеет свой стандартный вывод, открытый для канала ), и эта подоболочка ожидает завершения cmd, чтобы закончить (и сообщить о своем статусе выхода с помощьюexit).

Но это не должно быть необходимо с хорошим -поведением cmd. Это также не устранило бы случай, когда cmdразветвляется (и не ожидает )дочернего процесса с перенаправленным стандартным выводом, который может писать в свой стандартный вывод задолго после awkили даже после завершения этого сценария (. ] вероятно, более вероятный сценарий, чем команда, которая явно закрывает свой стандартный вывод ).

Если вывод cmdне является текстом, обратите внимание, что не все реализации awkмогут работать с байтом 0 или дополнительными -длинными строками, и в конце будет добавлен символ новой строки, если он еще не был на входе.

В наборе инструментов POSIX нет команды, которая может хранить произвольное количество двоичных данных в памяти и отображать их позже.

Если доступно perl, вы можете заменить команду awkтолько командой perl -0777 -pe ''.

Здесь вместо памяти вы можете сохранить выходные данные во временном файле, что решит проблему двоичного вывода и, вероятно, лучше масштабируется для больших выходных данных.

К сожалению,единственный надежный способ POSIX создать временный файл — это использовать утилиту m4, но эта утилита (, хотя она и предусмотрена POSIX ), не всегда встречается в производственных системах в наши дни. Вероятно, у вас больше шансов найти perl, чем m4.

Во всяком случае, это может быть:

die() {
  [ "$#" -eq 0 ] || printf >&2 '%s\n' "$@"
  exit 1
}

tmpdir=${TMPDIR:-/tmp}
tmpfile=$(
  echo 'mkstemp(TEMPLATE)' |
    m4 -D "TEMPLATE=${tmpdir%/}/XXXXXXX"
) && [ -n "$tmpfile" ] || die 'Cannot get a temp file'

{
  rm -f -- "$tmpfile" || die "Cannot remove $tmpfile"
  cmd 2>&1 >&3 3>&- 4<&-
  cat <&4
} 3> "$tmpfile" 4< "$tmpfile"

Здесь удаление связи с временным файлом после его открытия, но перед запуском cmdв качестве удобного способа очистки -.

Если вы ориентируетесь только на GNU (, помните, что «Linux» — это не ОС, а просто ядро, встречающееся в большом количестве ОС, в некоторых из которых даже нетsh)и систем OpenBSD, тогда вы должны иметь возможность использовать mktempвместо m4для создания временного файла.

5
28.04.2021, 23:16

Теги

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