Окрашивание ввода данных пользователем является трудным, потому что в половине случаев, это производится терминальным драйвером (с локальным эхом) так в этом случае, никакое приложение, работающее в том терминале, не может знать, когда пользователь собирается ввести текст и изменить цвет вывода соответственно. Только драйвер псевдотерминала (в ядре) знает (эмулятор терминала (как xterm) отправляет его, некоторые символы после некоторого нажатия клавиши и терминального драйвера могут передать некоторые символы обратно для эха, но xterm не может знать, является ли это от локального эха или от того, что приложение производило ведомой стороне псевдотерминала).
И затем, существует другой режим, где терминальному драйверу говорят не отозваться эхом, но приложение на этот раз производит что-то. Приложение (как те, которые используют readline как gdb, удар...), может отправить это на своем stdout или stderr, который будет трудным дифференцироваться от чего-то, что это производит для других вещей, чем реагирование на ввод данных пользователем.
Затем для дифференциации stdout приложения от его stderr существует несколько подходов.
Многие из них включают перенаправление команд stdout и stderr к каналам и тем каналам, считанным приложением для окраски его. Существует две проблемы с этим:
Другой подход должен изменить приложение так, чтобы это действительно окрасило свой stdout и stdin. Это часто не возможно или реалистично сделать.
Затем прием (для динамично связанных приложений) может быть должен угнать (использование $LD_PRELOAD
как в ответе sickill) функции вывода, вызванные приложением, чтобы произвести что-то и включать код в них, который выбирает основной цвет на основе того, ли предназначены они для вывода чего-то на stderr или stdout. Однако это означает угонять каждую возможную функцию из библиотеки C и любой другой библиотеки, которая делает a write(2)
syscall, непосредственно названный приложением, которое может потенциально закончить тем, что писало что-то на stdout или stderr (printf, помещает, perror...), и даже затем, который может изменить его поведение.
Другой подход мог быть должен использовать приемы PTRACE как strace
или gdb
сделайте для сцепления каждый раз write(2)
системный вызов называют и установил цвет вывода на основе ли write(2)
находится на дескрипторе файла 1 или 2.
Однако это - вполне большая вещь сделать.
Прием, с которым я просто играл, должен угнать strace
самостоятельно (который делает грязную работу сцепления себя перед каждым системным вызовом) использующий LD_PRELOAD, чтобы сказать этому изменять цвет вывода на основе того, обнаружило ли это a write(2)
на fd 1 или 2.
От взгляда на strace
исходный код, мы видим, что все, что он производит, сделано через vfprintf
функция. Все, что мы должны сделать, должно угнать ту функцию.
Обертка LD_PRELOAD была бы похожа:
#define _GNU_SOURCE
#include
#include
#include
#include
#include
int vfprintf(FILE *outf, const char *fmt, va_list ap)
{
static int (*orig_vfprintf) (FILE*, const char *, va_list) = 0;
static int c = 0;
va_list ap_orig;
va_copy(ap_orig, ap);
if (!orig_vfprintf) {
orig_vfprintf = (int (*) (FILE*, const char *, va_list))
dlsym (RTLD_NEXT, "vfprintf");
}
if (strcmp(fmt, "%ld, ") == 0) {
int fd = va_arg(ap, long);
switch (fd) {
case 2:
write(2, "\e[31m", 5);
c = 1;
break;
case 1:
write(2, "\e[32m", 5);
c = 1;
break;
}
} else if (strcmp(fmt, ") ") == 0) {
if (c) write(2, "\e[m", 3);
c = 0;
}
return orig_vfprintf(outf, fmt, ap_orig);
}
Затем мы компилируем его с:
cc -Wall -fpic -shared -o wrap.so wrap.c -ldl
И используйте его как:
LD_PRELOAD=/path/to/wrap.so strace -qfo /dev/null -e write -s 0 env -u LD_PRELOAD some-cmd
Вы заметите как, если Вы замените some-cmd
с bash
, подсказка удара и что Вы вводите, появляется в красном (stderr) в то время как с zsh
это появляется в черном цвете (потому что zsh копирует stderr на новый fd для отображения его подсказки и эха).
Это, действительно кажется, работает удивительно хорошо даже на приложения, которые Вы ожидали бы не (как те, которые действительно используют цвета).
Окрашивающий режим производится на strace
stderr, который, как предполагается, является терминалом. Если приложение перенаправит свой stdout или stderr, то наш угнанный strace будет продолжать писать окрашивающие escape-последовательности на терминале.
То решение имеет свои ограничения:
strace
: проблемы производительности, Вы не можете выполнить другие команды PTRACE как strace
или gdb
в нем, или проблемы setuid/setgidwrite
s на stdout/stderr каждого отдельного процесса. Так, например, в sh -c 'echo error >&2'
, error
было бы зеленым потому что echo
производит его на его stdout (какой sh, перенаправленный к stderr sh, но весь strace видит, является a write(1, "error\n", 6)
). И в sh -c 'seq 1000000 | wc'
, seq
делает много или write
s к ее stdout, таким образом, обертка закончится вывод много (невидимых) escape-последовательностей к терминалу.Я закончил ls
- парсинг:
is_group_writable() {
local ls="$(ls -ld "$1")"
[ "${ls:5:1}" = "w" ]
}
Я благодарен за обширный ответ @slm, но ни один из других методов не так прост. stat
метод потребовал бы, чтобы я записал по крайней мере два различных вызова для покрытия OS X, и даже после этого он возвращает восьмеричное представление, к которому я все еще должен применить арифметику. Метод Perl в порядке, но требует, чтобы я запустил интерпретатор для такой простой задачи, и я хотел бы сохранить это простым и использующим только posix utils.
Вы могли использовать stat
команда для получения битов полномочий. stat
доступно на большей части Unixes (OSX, BSD, не AIX от того, что я могу найти). Это должно работать над большей частью Unixes, кроме OSX & BSD от того, что я могу найти.
$ stat -c "%a" <file>
$ ls -l a
-rw-rw-r-- 1 saml saml 155 Oct 6 14:16 afile.txt
Используйте эту команду:
$ stat -c "%a" afile.txt
664
И используйте простое grep
видеть, является ли режим полномочий групп 6 или 7.
$ stat -c "%a" afile.txt | grep ".[67]."
Для OSX и BSD необходимо было бы использовать эту форму stat
, stat -f
(или возможно stat -x
), и синтаксический анализ соответственно. Начиная с опций к stat
отличаются, Вы могли перенести эту команду в lsb_release -a
управляйте и назовите соответствующую версию на основе ОС. Не идеальный, но осуществимый. Поймите это lsb_release
только доступно для дистрибутивов Linux, таким образом, другая альтернатива должна была бы быть создана для тестирования других Ose Unix.
Я думаю, что эта команда могла бы обслуживать Вас лучше все же. Я использую find
и printf
переключатель.
$ find a -prune -printf '%m\n'
664
Perl мог бы быть более портативным способом сделать это в зависимости от Ose, которые Вы пытаетесь покрыть.
$ perl -le '@pv=stat("afile.txt"); printf "%04o", $pv[2] & 07777;'
0664
Примечание: Вышеупомянутое использует Perl stat()
функционируйте для запросов битов файловой системы.
Можно сделать это более компактным путем воздержания от использования массива, @pv
, и имея дело с выводом stat()
непосредственно:
$ perl -le 'printf "%04o", (stat("a"))[2] & 07777;'
0664
stat
команда значительно различается среди различных систем и не является портативной.
– jordanm
26.10.2013, 04:34
find . -printf
может сделать это, но не может определить, доступно ли это на OSX', находят.
– slm♦
26.10.2013, 04:40
Одна опция состоит в том, чтобы использовать perl
, который будет портативным везде perl
доступно. Трудно найти систему Unix без него. Вот пример того, как он может быть интегрирован в сценарий оболочки:
if perl -e 'use Fcntl ":mode"; exit( ((stat("file.txt"))[2] & S_IWGRP) >> 3)'; then
echo "file is group writable"
else
echo "file is not group witeable"
fi
Подобный perl
примеры могут быть найдены в статистике perldoc.
FILE="filename";G=$(stat -c '%a' ${FILE} | cut -c2);
if [ $G -gt 5 ];then echo "Group write is set on ${FILE}";fi
Все стандартные инструменты Unix/Linux.
Я полагаю, что ВОЗМОЖНО иметь групповую запись, но не групповое чтение. В этом случае это будет либо оператор case 2|3|6|7 ), либо grep '[2|3|6|7]'
.
perms=$(ls -ld -- "$1"); perms=${perms%% *}; case $perms in ?????w????) …
– Gilles 'SO- stop being evil' 30.10.2013, 01:14