ZB Kommandozeile:
test.sh arg1 | grep "xyz"
Ist es möglich, die vollständige Befehlszeile einschließlich des folgenden grep im Bash-Skript test.sh abzurufen?
[ -t 1 ]
unix.stackexchange.com/a/401938/70524
ZB Kommandozeile:
test.sh arg1 | grep "xyz"
Ist es möglich, die vollständige Befehlszeile einschließlich des folgenden grep im Bash-Skript test.sh abzurufen?
[ -t 1 ]
unix.stackexchange.com/a/401938/70524
Antworten:
Es gibt im Allgemeinen keine Möglichkeit, dies zu tun .
Eine interaktive bash
Shell kann jedoch den Verlaufsmechanismus und die DEBUG
Falle nutzen, um den Befehlen, über die sie ausgeführt wird, über eine Umgebungsvariable die vollständige Befehlszeile mitzuteilen, zu der sie gehört:
$ 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}
Nein
bash (oder Ihre Shell) gibt zwei verschiedene Befehle aus.
test.sh arg1
grep "xyz"
test.sh
Ich konnte nicht wissen, wie ich grep folge.
Sie könnten jedoch durch Testen wissen, dass Sie sich "in" einem Rohr befinden /proc/self/fd/1
test.sh
#!/bin/bash
file /proc/self/fd/1
welche laufen als
> ./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]
(Bearbeiten) Siehe Murus Kommentar darüber, ob Sie sich auf einer Pfeife befinden.
Sie müssen nicht wissen, ob Sie dafür in einer Pfeife sind. Überprüfen Sie einfach, ob die Ausgabe ein TTY ist.
[ -t 1 ]
https://unix.stackexchange.com/a/401938/70524
Mithilfe von /proc/self/fd
können Sie sehen, ob Sie sich in einer Pipeline befinden, sowie eine ID für die Pipe. Wenn Sie /proc/\*/fd
nach dem passenden Rohr suchen, finden Sie die PID am anderen Ende des Rohrs. Mit der PID können Sie dann /proc/$PID/cmdline
den Vorgang in den Dateideskriptoren lesen und wiederholen, um herauszufinden, in was er geleitet wird.
$ 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
Wenn Sie Glück haben, erhalten die verschiedenen Befehle in der Pipeline aufeinanderfolgende PIDs, was die Arbeit etwas erleichtert.
Ich habe eigentlich kein Skript dafür, aber ich habe das Konzept bewiesen.
Ein anderer Weg könnte der Zugriff auf die $BASH_COMMAND
automatische Variable sein, aber es ist von Natur aus volatil und schwierig, den gewünschten Wert zu erfassen.
Ich denke, Sie konnten es nur über ein abfangen eval
, was auch das Aufrufen Ihrer Befehlszeilen auf besondere Weise beinhaltet, wie in:
CMD="${BASH_COMMAND##* eval }" eval './test.sh arg1 | grep "xyz"'
Hier $BASH_COMMAND
wird es erweitert, während es gleichzeitig bis zum eval
Bit der Zeichenfolge gelöscht wird, und die Ergebniszeichenfolge wird somit in eine $CMD
Hilfsvariable "aufgenommen" .
Kleines Beispiel:
$ 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
$
Natürlich kann es auch funktionieren (eigentlich besser) beim Aufrufen von Skripten durch zB sh -c
oder bash -c
, wie in:
$
$ 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
$
Hier ohne die Variable zu löschen.
Danke für deine Antworten. Ich habe verschiedene Dinge getestet und bin zu folgendem Testskript gekommen:
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
Tests:
$ ./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.
Der Befehlszeilenverlauf funktioniert nur ohne Shebang in der obersten Zeile des Skripts. Ich weiß nicht, ob dies zuverlässig und auch auf anderen Systemen funktioniert.
Ich konnte die Ausgabe von "readlink" (oder "file", wie von Archemar vorgeschlagen) nicht unterdrücken, als der Status erfolgreich war ("/ dev / pts / 3"). Die Weiterleitung der Ausgabe an / dev / null oder an eine Variable würde zu Fehlfunktionen führen. Das wäre also keine Option für mich in einem Skript.
Der von muru erwähnte TTY-Check ist einfach und für einige Anwendungsfälle möglicherweise bereits ausreichend.
Bearbeiten: Mein Verdienst geht an mosvy, weil die Frage war, wie man die komplette Befehlszeile erhält und nicht nur, ob sich das Skript in einer Pipe befindet. Ich mag den einfachen Teil "fc -nl -0" in seiner Antwort, da keine weitere Systemkonfiguration erforderlich ist. Es ist keine 100-prozentige Lösung, aber dies ist nur für meinen persönlichen Gebrauch und daher ausreichend. Vielen Dank an alle anderen für Ihre Hilfe.
[ -t 0 ]
. Sie können also überprüfen, ob stdin oder stdout kein TTY ist, und entsprechend vorgehen.
if [ -p /dev/stdout ]; ...
(genau wie readlink /proc/self/fd/..
dies unter BSD nicht funktioniert).
echo -e
will das mit ziemlicher sicherheit nicht -e
. Sie benötigen weitere Testfälle, die in eine Datei umgeleitet und darin aufgerufen werden $(...)
. Ich möchte Sie jedoch dringend bitten, zu prüfen, ob dies eine gute Idee ist. Programme wie solche, ls
die ihre Ausgabe ändern, je nachdem, ob sie an ein tty oder eine Pipe ausgegeben werden, sind ärgerlich zu verwenden.