Например, командная строка:
test.sh arg1 | grep "xyz"
Можно ли получить полную командную строку, включая следующий grep в bash-скрипте test.sh?
[ -t 1 ] unix.stackexchange.com/a/401938/70524
Например, командная строка:
test.sh arg1 | grep "xyz"
Можно ли получить полную командную строку, включая следующий grep в bash-скрипте test.sh?
[ -t 1 ] unix.stackexchange.com/a/401938/70524
Ответы:
Там нет никакого способа сделать это в целом .
Но интерактивная bashоболочка может использовать механизм истории и DEBUGловушку, чтобы «сообщить» командам, которые она выполняет, полную командную строку, частью которой они являются, через переменную среды:
$ trap 'export LC=$(fc -nl -0); LC=${LC#? }' DEBUG
$ sh -c 'printf "last_command={%s}\n" "$LC"' | cat; true
last_command={sh -c 'printf "last_command={%s}\n" "$LC"' | cat; true}
нет
bash (или ваша оболочка) выполнит две разные команды.
test.sh arg1grep "xyz"test.sh не мог знать о следующем grep.
однако вы можете узнать, что находитесь «внутри» трубы, протестировав /proc/self/fd/1
test.sh
#!/bin/bash
file /proc/self/fd/1
который работает как
> ./test.sh
/proc/self/fd/1: symbolic link to /dev/pts/0
> ./test.sh | cat
/proc/self/fd/1: broken symbolic link to pipe:[25544239]
(Изменить) см . Комментарий Муру о том, что вы знаете, находитесь ли вы на трубе.
вам не нужно знать, если вы находитесь в трубе для этого. Просто проверьте, является ли вывод TTY.
[ -t 1 ]https://unix.stackexchange.com/a/401938/70524
Используя /proc/self/fd, вы можете увидеть, находитесь ли вы в конвейере, а также идентификатор для канала. Если вы выполняете /proc/\*/fdпоиск подходящего канала, вы можете найти PID другого конца канала. С помощью PID вы можете затем прочитать /proc/$PID/cmdlineи повторить процесс в своих файловых дескрипторах, чтобы найти, к чему он подключен.
$ cat | cat | cat &
$ ps
PID TTY TIME CMD
6942 pts/16 00:00:00 cat
6943 pts/16 00:00:00 cat
6944 pts/16 00:00:00 cat
7201 pts/16 00:00:00 ps
20925 pts/16 00:00:00 bash
$ ls -l /proc/6942/fd
lrwx------. 1 tim tim 64 Jul 24 19:59 0 -> /dev/pts/16
l-wx------. 1 tim tim 64 Jul 24 19:59 1 -> 'pipe:[49581130]'
lrwx------. 1 tim tim 64 Jul 24 19:59 2 -> /dev/pts/16
$ ls -l /proc/6943/fd
lr-x------. 1 tim tim 64 Jul 24 19:59 0 -> 'pipe:[49581130]'
l-wx------. 1 tim tim 64 Jul 24 19:59 1 -> 'pipe:[49581132]'
lrwx------. 1 tim tim 64 Jul 24 19:59 2 -> /dev/pts/16
$ ls -l /proc/6944/fd
lr-x------. 1 tim tim 64 Jul 24 19:59 0 -> 'pipe:[49581132]'
lrwx------. 1 tim tim 64 Jul 24 19:59 1 -> /dev/pts/16
lrwx------. 1 tim tim 64 Jul 24 19:59 2 -> /dev/pts/16
Также, если вам повезет, различные команды в конвейере получат последовательные идентификаторы PID, что сделает его немного проще.
На самом деле у меня нет сценария для этого, но я доказал концепцию.
Другим способом может быть доступ к $BASH_COMMANDавтоматической переменной, но она по своей природе нестабильна и трудно уловить требуемое значение.
Я думаю, что вы можете поймать его только через an eval, который также включает специальный вызов ваших командных строк, например:
CMD="${BASH_COMMAND##* eval }" eval './test.sh arg1 | grep "xyz"'
Здесь $BASH_COMMANDрасширяется, а также очищается до evalстроки, и результирующая строка, таким образом, «снимается» в вспомогательную $CMDпеременную.
Маленький пример:
$ cat test.sh
#!/bin/sh
printf 'you are running %s\n' "$CMD"
sleep 1
echo bye bye
$
$ CMD="${BASH_COMMAND##* eval }" eval './test.sh | { grep -nH "."; }'
(standard input):1:you are running './test.sh | { grep -nH "."; }'
(standard input):2:bye bye
$
Естественно, он также может работать (на самом деле лучше) при вызове скриптов, например, sh -cили bash -c, как в:
$
$ CMD="${BASH_COMMAND}" sh -c './test.sh | { grep -nH "."; }'
(standard input):1:you are running CMD="${BASH_COMMAND}" sh -c './test.sh | { grep -nH "."; }'
(standard input):2:bye bye
$
Здесь без очистки переменной.
Спасибо за ваши ответы. Я проверил разные вещи и пришел к следующему сценарию тестирования:
test.sh:
hist=`fc -nl -0`
# remove leading and trailing whitespaces
hist="$(echo "${hist}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
echo "Command line from history: '$hist'"
if [ -t 1 ]; then
echo "Direct output to TTY, no pipe involved."
else
echo "No TTY, maybe a piped command."
fi
if [ -p /dev/stdout ]; then
echo "stdout is a pipe."
else
echo "stdout is not a pipe."
fi
readlink -e /proc/self/fd/1
rst=$?
if [ $rst -eq 0 ]; then
echo "Readlink test status okay, no pipe involved."
else
echo "Readlink test status error $rst, maybe a piped command."
fi
тесты:
$ ./test.sh test1
Command line from history: './test.sh test1'
Direct output to TTY, no pipe involved.
stdout is not a pipe.
/dev/pts/3
Readlink test status okay, no pipe involved.
$ ./test.sh test2 | cat
Command line from history: './test.sh test2 | cat'
No TTY, maybe a piped command.
stdout is a pipe.
Readlink test status error 1, maybe a piped command.
$ echo "another command before pipe doesn't matter" | ./test.sh test3
Command line from history: 'echo "another command before pipe doesn't matter" | ./test.sh test3'
Direct output to TTY, no pipe involved.
stdout is not a pipe.
/dev/pts/3
Readlink test status okay, no pipe involved.
История командной строки работает только без Шебанга в верхней строке скрипта. Не знаю, будет ли это работать надежно и на других системах.
Я не смог подавить вывод из «readlink» (или «file», как предложено в Archemar), когда статус был успешным («/ dev / pts / 3»). Передача вывода в / dev / null или в переменную может привести к сбоям. Так что это не будет вариант для меня в сценарии.
Проверка TTY, о которой говорилось в muru, проста и, возможно, уже достаточна для некоторых случаев использования.
Редактировать: Моя заслуга в Mosvy, потому что вопрос был в том, как получить полную командную строку, а не просто определить, находится ли сценарий на конвейере. Мне нравится простая часть "fc -nl -0" в его ответе, потому что дальнейшая настройка системы не требуется. Это не 100-процентное решение, но это только для моего личного использования и, следовательно, достаточно. Спасибо всем остальным за вашу помощь.
[ -t 0 ]. Таким образом, вы можете проверить, является ли stdin или stdout TTY и действовать соответствующим образом.
if [ -p /dev/stdout ]; ...(точно так же, как readlink /proc/self/fd/..это не работает в BSD).
echo -eпочти наверняка не хочет -e. Вам нужно больше тестов, перенаправляющих в файл, вызываемых внутри $(...). Однако я призываю вас подумать, если это хорошая идея. Программы, lsкоторые меняют свой вывод в зависимости от того, выводят ли они на tty или канал, раздражают.