Я хотел бы написать небольшую обертку вокруг ps
, чтобы независимо от того, какие параметры или если я выполню grep результат, я мог бы получить первую строку вывода ps
, который говорит мне, что это за столбцы.
Например, вывод ps_wrapper -aux | grep thing
будет выглядеть примерно так:
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
name 33925 1.0 0.0 9972 5528 pts/0 Ss 19:34 0:00 /bin/thing
Я попытался посмотреть, может ли tee
быть полезным, и попробовал что-то вроде
function ps_wrapper {
tmpfile=$(mktemp)
ps $@ > $tmpfile
head -n 1 $tmpfile >> /dev/stdout
cat $tmpfile
}
, но вскоре я понял это, конечно, даже если я написал " вручную" в /dev/stdout
, он все равно будет передан в grep
.
Я хочу напечатать первую строку фактической команды, которую я запускаю, потому что столбцы, напечатанные ps
, могут меняться в зависимости от используемых параметров.
Я использую zsh, но я не возражаю против использования другого инструмента, если есть более подходящий.
Спасибо, что прочитали.
В общем случае невозможно «обойти канал», например, отправить вывод туда, куда бы он направлялся, если бы канала не было. Однако можно обойти канал в смысле перенаправления вывода в другое место по вашему выбору, например, на терминал. Специальный файл /dev/tty
всегда представляет текущий терминал.
function ps_wrapper {
tmpfile=$(mktemp)
ps "$@" > $tmpfile
head -n 1 $tmpfile >/dev/tty
cat $tmpfile
rm $tmpfile
}
Также возможно обойти конвейер, если вы «сохраните» исходное местоположение и передадите его через файловый дескриптор . Но вы не можете сделать это из функции ps_wrapper
.
function ps_wrapper {
tmpfile=$(mktemp)
ps "$@" > $tmpfile
head -n 1 $tmpfile >&3
cat $tmpfile
rm $tmpfile
}
{ ps_wrapper … | grep …; } 3>&1
Существует множество способов избежать создания временного файла. Я упомяну несколько. Если не указано иное, решения в этом ответе работают в bash, если вы добавите двойные кавычки вокруг подстановок переменных и команд .
Если вы хотите изменить способ вызова функции, вы можете вызывать head
и grep
последовательно с правой -стороны конвейера. head
прочитает и напечатает первую строку, а остальную часть оставит для последующего использования.
ps … | { head -n 1; grep …; }
Вы можете заменить процесс с помощью tee
или zsh, встроенного -вtee
-подобно функции (multios
), чтобы дублировать вывод, отправляя один поток на head -n 1
и другой команде по вашему выбору. Однако, если вы просто направляете каждый поток в команду, между ними будет гонка, и если head
недостаточно быстро, первая строка может не оказаться наверху. Это, вероятно, часто будет работать, потому что head
довольно быстро, но нет никакой гарантии, например, если grep
находится в кеше диска, а head
нет.
ps | tee >(head -n 1 >/dev/tty) | grep …
ps >(head -n 1 >/dev/tty) | grep … # zsh only, only if multios is not disabled
Вы можете использовать awk для отображения первой строки, а затем передать остальные.
function ps_wrapper {
ps "$@" | awk 'NR == 1 {print >"/dev/tty"} NR != 1 {print}'
}
Пояснения:
NR
— номер строки.print
без аргумента выводит строку ввода. Другим подходом было бы встроить функциональность фильтра в единую команду. Выберите строку, которая не будет отображаться в аргументах ps
, чтобы использовать ее в качестве разделителя, например |
.
function pipe_preserving_first_line {
local lhs
lhs=()
while [[ $1 != '|' ]]; do
lhs+=($1)
shift
done
shift
"${lhs[@]}" | {
head -n 1;
"$@"
}
}
pipe_preserving_first_line ps u \| grep foo
Вместо использования grep вы можете использовать средства сопоставления ps
, такие как -C
для сопоставления процесса по его имени команды.
ps uww -C mycommand
Вместо использования grep вы можете использовать средства сопоставления pgrep
.
ps -p $(pgrep -d, mycommand)