Ví dụ: dòng lệnh:
test.sh arg1 | grep "xyz"
Có thể lấy dòng lệnh hoàn chỉnh bao gồm grep sau trong tập lệnh bash test.sh không?
[ -t 1 ]
unix.stackexchange.com/a/401938/70524
Ví dụ: dòng lệnh:
test.sh arg1 | grep "xyz"
Có thể lấy dòng lệnh hoàn chỉnh bao gồm grep sau trong tập lệnh bash test.sh không?
[ -t 1 ]
unix.stackexchange.com/a/401938/70524
Câu trả lời:
Không có cách nào để làm điều đó nói chung .
Nhưng một bash
lớp vỏ tương tác có thể tận dụng cơ chế lịch sử và DEBUG
cái bẫy để "nói" các lệnh mà nó chạy dòng lệnh hoàn chỉnh mà chúng là một phần của biến môi trường:
$ 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}
Không
bash (hoặc shell của bạn) sẽ rẽ nhánh hai lệnh riêng biệt.
test.sh arg1
grep "xyz"
test.sh
không thể biết về việc theo grep.
tuy nhiên bạn có thể biết bạn đang "bên trong" một đường ống bằng cách thử nghiệm /proc/self/fd/1
kiểm tra
#!/bin/bash
file /proc/self/fd/1
mà chạy như
> ./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]
(Chỉnh sửa) xem nhận xét của muru về việc biết bạn đang ở trên đường ống.
bạn không cần phải biết nếu bạn đang ở trong một đường ống cho điều đó. Chỉ cần kiểm tra nếu đầu ra là một TTY.
[ -t 1 ]
https://unix.stackexchange.com/a/401938/70524
Bằng cách sử dụng /proc/self/fd
, bạn có thể xem liệu bạn đang ở trong một đường ống cũng như ID cho đường ống. Nếu bạn lặp đi lặp lại thông qua việc /proc/\*/fd
tìm kiếm đường ống phù hợp, bạn có thể tìm thấy PID của đầu kia của ống. Với PID, sau đó bạn có thể đọc /proc/$PID/cmdline
cũng như lặp lại quy trình trên các bộ mô tả tệp của nó để tìm ra những gì nó được đưa vào.
$ 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
Ngoài ra nếu bạn may mắn, các lệnh khác nhau trong đường ống sẽ nhận được các PID liên tiếp, điều này sẽ giúp việc này dễ dàng hơn một chút.
Tôi thực sự không có một kịch bản để làm điều này, nhưng tôi đã chứng minh khái niệm này.
Một cách khác có thể là bằng cách truy cập $BASH_COMMAND
biến tự động, nhưng vốn dĩ nó rất dễ bay hơi và khó nắm bắt được giá trị mong muốn.
Tôi nghĩ rằng bạn chỉ có thể bắt nó chỉ thông qua một eval
, điều này cũng liên quan đến việc gọi các dòng lệnh của bạn theo một cách đặc biệt, như trong:
CMD="${BASH_COMMAND##* eval }" eval './test.sh arg1 | grep "xyz"'
Ở đây $BASH_COMMAND
được mở rộng trong khi cũng thanh lọc nó lên đến eval
bit của chuỗi và do đó chuỗi kết quả được "bẻ khóa" thành một $CMD
biến của trình trợ giúp .
Ví dụ nhỏ:
$ 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
$
Đương nhiên nó cũng có thể làm việc (thực sự tốt hơn) trong khi cách gọi script bằng ví dụ sh -c
hoặc bash -c
, như trong:
$
$ 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
$
Ở đây mà không thanh trừng các biến.
Cảm ơn câu trả lời của bạn. Tôi đã thử nghiệm những thứ khác nhau và đến với kịch bản thử nghiệm sau:
kiểm tra
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
Các xét nghiệm:
$ ./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.
Lịch sử dòng lệnh chỉ hoạt động mà không có Shebang ở dòng trên cùng của tập lệnh. Không biết điều này có hoạt động đáng tin cậy không và trên các hệ thống khác.
Tôi không thể chặn đầu ra từ "readlink" (hoặc "tệp" như được đề xuất từ Archemar), khi trạng thái thành công ("/ dev / pts / 3"). Đầu ra đường ống đến / dev / null hoặc đến một biến sẽ dẫn đến trục trặc. Vì vậy, đây sẽ không phải là một lựa chọn cho tôi trong một kịch bản.
Kiểm tra TTY mà muru đề cập là dễ dàng và có thể đã đủ cho một số trường hợp sử dụng.
Chỉnh sửa: Tín dụng của tôi chuyển sang mosvy, bởi vì câu hỏi là làm thế nào để có được dòng lệnh hoàn chỉnh và không chỉ để xác định xem tập lệnh có nằm trên đường ống hay không. Tôi thích phần đơn giản "fc -nl -0" trong câu trả lời của anh ấy, bởi vì không cần cấu hình hệ thống nữa. Nó không phải là một giải pháp 100 phần trăm, nhưng điều này chỉ dành cho sử dụng cá nhân của tôi và do đó là đủ. Cảm ơn tất cả những người khác đã giúp đỡ của bạn.
[ -t 0 ]
. Vì vậy, bạn có thể kiểm tra xem stdin hoặc stdout không phải là TTY và tiến hành tương ứng.
if [ -p /dev/stdout ]; ...
(giống như readlink /proc/self/fd/..
điều này không hoạt động trên BSD).
echo -e
gần như chắc chắn không muốn -e
. Bạn cần nhiều trường hợp kiểm tra hơn, chuyển hướng đến một tệp, được gọi bên trong $(...)
. Tuy nhiên tôi sẽ khuyến khích bạn xem xét nếu đây là một ý tưởng tốt. Các chương trình như ls
thay đổi đầu ra của chúng tùy thuộc vào việc chúng xuất ra một tty hay một đường ống gây khó chịu khi sử dụng.