考虑以下简化示例:
my_prog|awk '...' > output.csv &
my_pid="$!" #Gives the PID for awk instead of for my_prog
sleep 10
kill $my_pid #my_prog still has data in its buffer that awk never saw. Data is lost!
在bash中,$my_pid
指向awk
的PID。但是,我需要my_prog
的PID。如果我杀了awk
,my_prog
不知道刷新它的输出缓冲区并且数据丢失。那么,如何获得my_prog
的PID?请注意ps aux|grep my_prog
无效,因为可能会有多个my_prog
。
注意:将cat
更改为awk '...'
以帮助澄清我需要的内容。
答案 0 :(得分:8)
刚刚遇到同样的问题。我的解决方案:
process_1 | process_2 &
PID_OF_PROCESS_2=$!
PID_OF_PROCESS_1=`jobs -p`
确保process_1是第一个后台进程。否则,您需要解析jobs -l
的完整输出。
答案 1 :(得分:5)
我能够使用mkfifo
显式命名管道来解决它。
第1步:mkfifo capture
。
第2步:运行此脚本
my_prog > capture &
my_pid="$!" #Now, I have the PID for my_prog!
awk '...' capture > out.csv &
sleep 10
kill $my_pid #kill my_prog
wait #wait for awk to finish.
我不喜欢有mkfifo的管理。希望有人有一个更容易的解决方案。
答案 2 :(得分:5)
这是一个没有包装器或临时文件的解决方案。这仅适用于背景管道,其输出从包含脚本的stdout捕获,如您的情况。假设您想要这样做:
cmd1 | cmd2 | cmd3 >pipe_out &
# do something with PID of cmd2
如果只有bash可以提供${PIPEPID[n]}
!!我找到的替代“黑客”如下:
PID=$( { cmd1 | { cmd2 0<&4 & echo $! >&3 ; } 4<&0 | cmd3 >pipe_out & } 3>&1 | head -1 )
如果需要,您还可以分别使用cmd*
和cmd2
关闭fd 3(适用于3>&-
)和fd 4(适用于4<&-
)。如果您这样做,对于cmd2
,请确保在之后仅关闭fd 4 ,从中重定向fd 0。
答案 3 :(得分:4)
在命令周围添加shell包装并捕获pid。对于我的例子,我使用iostat。
#!/bin/sh
echo $$ > /tmp/my.pid
exec iostat 1
Exec用保留pid的新进程替换shell。
test.sh | grep avg
虽然如此:
$ cat my.pid
22754
$ ps -ef | grep iostat
userid 22754 4058 0 12:33 pts/12 00:00:00 iostat 1
所以你可以:
sleep 10
kill `cat my.pid`
这样更优雅吗?
答案 4 :(得分:3)
使用oneliner改进@Marvin和@Nils Goroll的答案,将管道中所有命令的pids提取到shell数组变量中:
Log
在我的解决方案onCreate
中包含String name;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.main_menu);
final SharedPreferences shared = getSharedPreferences("PrefName", Context.MODE_PRIVATE);
final SharedPreferences.Editor editor = shared.edit();
final int Coins = shared.getInt("CoinsValue", 1000);
final boolean game = (shared.getBoolean("game", true));
// 1. Instantiate an AlertDialog.Builder with its constructor
AlertDialog.Builder builder = new AlertDialog.Builder(MainMenu.this);
// 2. Chain together various setter methods to set the dialog characteristics
builder.setMessage("Unlock a new set to have more fun")
.setTitle("UNLOCK SET");
// Add the buttons
builder.setPositiveButton("100 coins", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
if (Coins >= 100){
editor.putInt(name, false).apply();
Toast.makeText(MainMenu.this, "Set Unlocked", Toast.LENGTH_SHORT).show();
coinsValue = Coins - 100;
editor.putInt("CoinsValue", coinsValue);
editor.apply();
Log.e("Msg", "" + name + " value = " + deathparade);
}
}
});
builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// User cancelled the dialog
}
});
// 3. Get the AlertDialog from create()
final AlertDialog dialog = builder.create();
gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
switch (position){
case 9:
Log.e("Msg", "Game value = " + game);
if(game){
name = "game";
editor.putString("nameLock", "game");
editor.apply();
Log.e("Msg", "" + name);
dialog.show();
} else {
editor.putInt("ArrayValue", R.array.game);
editor.putInt("DrawableValue", R.drawable.game_bg);
editor.apply();
startActivity(new Intent(MainMenu.this, MainActivity.class));
}
break;
中通常可用的值。请注意使用# run some command
ls -l | rev | sort > /dev/null &
# collect pids
pids=(`jobs -l % | egrep -o '^(\[[0-9]+\]\+| ) [ 0-9]{5} ' | sed -e 's/^[^ ]* \+//' -e 's! $!!'`)
# use them for something
echo pid of ls -l: ${pids[0]}
echo pid of rev: ${pids[1]}
echo pid of sort: ${pids[2]}
echo pid of first command e.g. ls -l: $pids
echo pid of last command e.g. sort: ${pids[-1]}
# wait for last command in pipe to finish
wait ${pids[-1]}
仅输出“当前”作业,默认情况下是最后一个作业。
示例输出:
${pids[-1]}
更新2017-11-13:改进了$!
命令,该命令可以更好地处理复杂(多行)命令。
答案 5 :(得分:2)
根据您的评论,我仍然无法理解为什么您更愿意以有序的方式杀死my_prog
。在多处理系统上,十秒是一个非常随意的测量,my_prog
可以产生10k行或0行输出,具体取决于系统负载。
如果您想将my_prog
的输出限制为更确定的尝试
my_prog | head -1000 | awk
没有脱离shell。在最坏的情况下,head将关闭其输入,my_prog将获得SIGPIPE。在最好的情况下,请更改my_prog
,以便为您提供所需的输出量。
在回复评论时添加:
只要你控制my_prog
,就可以给它一个可选的-s duration
参数。然后在主循环中的某个位置放置谓词:
if (duration_exceeded()) {
exit(0);
}
其中exit将依次正确刷新输出文件。如果绝望并且没有地方放置谓词,这可以使用alarm(3)来实现,我故意不会显示因为它很糟糕。
麻烦的核心是my_prog
永远运行。这里的其他所有东西都是克服这种限制的黑客。
答案 6 :(得分:2)
灵感来自@ Demosthenex的答案:使用子壳:
$ ( echo $BASHPID > pid1; exec vmstat 1 5 ) | tail -1 &
[1] 17371
$ cat pid1
17370
$ pgrep -fl vmstat
17370 vmstat 1 5
答案 7 :(得分:0)
我拼命寻找好的解决方案,从管道工作中获取所有PID,并且一种有希望的方法失败了(参见此答案的先前修订版)。
所以,不幸的是,我能想到的最好的就是使用GNU awk解析jobs -l
输出:
function last_job_pids {
if [[ -z "${1}" ]] ; then
return
fi
jobs -l | awk '
/^\[/ { delete pids; pids[$2]=$2; seen=1; next; }
// { if (seen) { pids[$1]=$1; } }
END { for (p in pids) print p; }'
}
答案 8 :(得分:0)
我的解决方案是查询jobs
并使用perl
解析它
在后台启动两个管道:
$ sleep 600 | sleep 600 |sleep 600 |sleep 600 |sleep 600 &
$ sleep 600 | sleep 600 |sleep 600 |sleep 600 |sleep 600 &
查询后台作业:
$ jobs
[1]- Running sleep 600 | sleep 600 | sleep 600 | sleep 600 | sleep 600 &
[2]+ Running sleep 600 | sleep 600 | sleep 600 | sleep 600 | sleep 600 &
$ jobs -l
[1]- 6108 Running sleep 600
6109 | sleep 600
6110 | sleep 600
6111 | sleep 600
6112 | sleep 600 &
[2]+ 6114 Running sleep 600
6115 | sleep 600
6116 | sleep 600
6117 | sleep 600
6118 | sleep 600 &
解析第二个作业%2
的作业列表。解析可能容易出错,但在这些情况下它可以正常工作。我们的目标是捕获第一个数字,然后是空格。它使用括号
pids
中
$ pids=($(jobs -l %2 | perl -pe '/(\d+) /; $_=$1 . "\n"'))
$ echo $pids
6114
$ echo ${pids[*]}
6114 6115 6116 6117 6118
$ echo ${pids[2]}
6116
$ echo ${pids[4]}
6118
对于第一个管道:
$ pids=($(jobs -l %1 | perl -pe '/(\d+) /; $_=$1 . "\n"'))
$ echo ${pids[2]}
6110
$ echo ${pids[4]}
6112
我们可以将它包装成一个小别名/函数:
function pipeid() { jobs -l ${1:-%%} | perl -pe '/(\d+) /; $_=$1 . "\n"'; }
$ pids=($(pipeid)) # PIDs of last job
$ pids=($(pipeid %1)) # PIDs of first job
我已在bash
和zsh
中对此进行了测试。不幸的是,在bash
中,我无法将pipeid的输出传递给另一个命令。可能是因为该管道在子shell中运行而无法查询作业列表??