Ad esempio riga di comando:
test.sh arg1 | grep "xyz"
È possibile ottenere la riga di comando completa incluso il seguente grep nello script bash test.sh?
[ -t 1 ] unix.stackexchange.com/a/401938/70524
Ad esempio riga di comando:
test.sh arg1 | grep "xyz"
È possibile ottenere la riga di comando completa incluso il seguente grep nello script bash test.sh?
[ -t 1 ] unix.stackexchange.com/a/401938/70524
Risposte:
Non c'è modo di farlo in generale .
Ma una bashshell interattiva può sfruttare il meccanismo della cronologia e la DEBUGtrap per "dire" i comandi di cui esegue l'intera riga di comando di cui fanno parte tramite una variabile di ambiente:
$ 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}
no
bash (o la tua shell) eseguirà il fork di due comandi distinti.
test.sh arg1grep "xyz"test.sh non potevo sapere di seguire grep.
potresti comunque sapere di essere "dentro" una pipa testando /proc/self/fd/1
test.sh
#!/bin/bash
file /proc/self/fd/1
che funzionano come
> ./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]
(Modifica) vedi il commento di Muru sul sapere se sei su una pipa.
non hai bisogno di sapere se sei in una pipa per questo. Basta controllare se l'output è un TTY.
[ -t 1 ]https://unix.stackexchange.com/a/401938/70524
Usando /proc/self/fd, puoi vedere se sei in una pipeline e un ID per la pipa. Se si esegue la /proc/\*/fdricerca della pipe corrispondente, è possibile trovare il PID dell'altra estremità della pipe. Con il PID, puoi quindi leggere /proc/$PID/cmdlinee ripetere il processo sui suoi descrittori di file per trovare in cosa viene eseguito il piping.
$ 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
Inoltre, se sei fortunato, i diversi comandi nella pipeline riceveranno PID consecutivi che lo renderanno un po 'più semplice.
In realtà non ho una sceneggiatura per farlo, ma ho dimostrato il concetto.
Un altro modo potrebbe essere accedendo alla $BASH_COMMANDvariabile automatica, ma è intrinsecamente volatile e difficile ottenere il valore desiderato.
Penso che potresti prenderlo solo tramite un eval, che implica anche invocare le tue righe di comando in un modo speciale, come in:
CMD="${BASH_COMMAND##* eval }" eval './test.sh arg1 | grep "xyz"'
Qui $BASH_COMMANDviene espanso pur eliminandolo fino al evalbit di stringa e la stringa risultante viene quindi "istantanea" in una $CMDvariabile helper .
Piccolo esempio:
$ 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
$
Naturalmente può anche funzionare (in realtà meglio) mentre invoca gli script per esempio sh -co bash -c, come 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
$
Qui senza eliminare la variabile.
Grazie per le tue risposte Ho testato diverse cose e sono arrivato al seguente script di test:
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:
$ ./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.
La cronologia della riga di comando funziona solo senza Shebang nella riga superiore dello script. Non so se funzionerà in modo affidabile e anche su altri sistemi.
Non sono riuscito a sopprimere l'output di "readlink" (o "file" come suggerito da Archemar), quando lo stato ha avuto esito positivo ("/ dev / pts / 3"). Il piping dell'output su / dev / null o su una variabile comporterebbe un malfunzionamento. Quindi questa non sarebbe un'opzione per me in una sceneggiatura.
Il controllo TTY che Muru ha menzionato è facile ed è forse già sufficiente per alcuni casi d'uso.
Modifica: il mio merito va a mosvy, perché la domanda era come ottenere la riga di comando completa e non solo per determinare se lo script è su una pipe. Mi piace la semplice parte "fc -nl -0" nella sua risposta, perché non è necessaria alcuna ulteriore configurazione del sistema. Non è una soluzione al 100 percento, ma è solo per uso personale e quindi sufficiente. Grazie a tutti gli altri per il vostro aiuto.
[ -t 0 ]. Quindi puoi verificare se stdin o stdout non sono un TTY e procedere di conseguenza.
if [ -p /dev/stdout ]; ...(proprio come readlink /proc/self/fd/..questo non funziona su BSD).
echo -equasi certamente non vuole il -e. Hai bisogno di più casi di test, reindirizzamento a un file, invocazione all'interno $(...). Tuttavia, ti esorto a considerare se questa è una buona idea. Programmi come i lsquali cambiano il loro output a seconda che siano in uscita su un tty o su una pipe sono fastidiosi da usare.