我正在使用:
pgrep -P $$
获取$$的孩子pid。但是我实际上也想要一个孙辈和曾孙辈的名单。
我该怎么做?使用常规的编程语言,例如,我们可以使用递归来实现,但是使用bash?也许使用bash函数?
答案 0 :(得分:3)
我已经发布了attempted solution。它简短有效,并且似乎符合OP的问题,因此我将其保留不变。但是,它存在一些性能和可移植性问题,这意味着它不是一个很好的通用解决方案。这段代码试图解决问题:
top_pid=$1
# Make a list of all process pids and their parent pids
ps_output=$(ps -e -o pid= -o ppid=)
# Populate a sparse array mapping pids to (string) lists of child pids
children_of=()
while read -r pid ppid ; do
[[ -n $pid && pid -ne ppid ]] && children_of[ppid]+=" $pid"
done <<< "$ps_output"
# Add children to the list of pids until all descendants are found
pids=( "$top_pid" )
unproc_idx=0 # Index of first process whose children have not been added
while (( ${#pids[@]} > unproc_idx )) ; do
pid=${pids[unproc_idx++]} # Get first unprocessed, and advance
pids+=( ${children_of[pid]-} ) # Add child pids (ignore ShellCheck)
done
# Do something with the list of pids (here, just print them)
printf '%s\n' "${pids[@]}"
保留了使用广度优先搜索来构建树的基本方法,但是有关流程的基本信息是通过运行一次ps
(符合POSIX的)来获得的。 pgrep
不再使用,因为它不在POSIX中,并且可以运行多次。另外,从队列中删除项目(复制除其中一个元素之外的所有元素)的效率非常低下的方式已由操纵索引变量代替。
在我的老式Linux系统上使用pid 0在大约400个进程上运行时,平均(实际)运行时间为0.050s。
我仅在Linux上对其进行了测试,但是它仅使用Bash 3功能和ps
的POSIX兼容功能,因此它也应在其他系统上工作。
答案 1 :(得分:2)
仅使用bash内置函数(甚至不使用ps
或pgrep
!)
#!/usr/bin/env bash
collect_children() {
# format of /proc/[pid]/stat file; group 1 is PID, group 2 is its parent
stat_re='^([[:digit:]]+) [(].*[)] [[:alpha:]] ([[:digit:]]+) '
# read process tree into a bash array
declare -g children=( ) # map each PID to a string listing its children
for f in /proc/[[:digit:]]*/stat; do # forcing initial digit skips /proc/net/stat
read -r line <"$f" && [[ $line =~ $stat_re ]] || continue
children[${BASH_REMATCH[2]}]+="${BASH_REMATCH[1]} "
done
}
# run a fresh collection, then walk the tree
all_children_of() { collect_children; _all_children_of "$@"; }
_all_children_of() {
local -a immediate_children
local child
read -r -a immediate_children <<<"${children[$1]}"
for child in "${immediate_children[@]}"; do
echo "$child"
_all_children_of "$child"
done
}
all_children_of "$@"
在我的本地系统上,time all_children_of 1 >/dev/null
(在已经运行的shell中调用该功能)的时钟约为0.018s-通常在collect_children
阶段为0.013s(一次)读取进程树的操作),并为_all_children_of
的初始调用触发的树的递归遍历设置0.05s。
以前的时间仅测试步行所需的时间,而忽略了扫描所需的时间。
答案 2 :(得分:1)
下面的代码将打印当前进程及其所有后代的PID。它使用Bash数组作为队列来实现进程树的breadth-first search。
unprocessed_pids=( $$ )
while (( ${#unprocessed_pids[@]} > 0 )) ; do
pid=${unprocessed_pids[0]} # Get first elem.
echo "$pid"
unprocessed_pids=( "${unprocessed_pids[@]:1}" ) # Remove first elem.
unprocessed_pids+=( $(pgrep -P $pid) ) # Add child pids
done
答案 3 :(得分:0)
大概一个简单的循环就可以做到:
# set a value for pid here
printf 'Children of %s:\n' $pid
for child in $(pgrep -P $pid); do
printf 'Children of %s:\n' $child
pgrep -P $child
done
答案 4 :(得分:0)
如果pgrep
不能满足您的要求,您可以随时直接使用ps
。选项将在某种程度上取决于平台。
ps -o ppid,pid |
awk -v pid=$$ 'BEGIN { parent[pid] = 1 } # collect interesting parents
{ child[$2] = $1 } # collect parents of all processes
$1 == pid { parent[$2] = 1 }
END { for (p in child)
if (parent[child[p]])
print p }'
变量名不是正交的-parent
收集pid
或其子项之一作为键的过程,即“有趣的”父项,而child
包含父项每个流程,以流程为键,父流程为值。
答案 5 :(得分:0)
我最终使用node.js和bash做到了这一点:
// type that simply serves as a data container (replacing the ValueTuple)
private class Data : IEquatable<Data>
{
private const int maxNumberOfRandom = 3;
public readonly int[] Values1 = new int[maxNumberOfRandom];
public readonly int[] Values2 = new int[maxNumberOfRandom];
public bool IsNull { get; set; }
public bool Equals(Data other)
{
return CompareArrays(Values1, other.Values1) && CompareArrays(Values2, other.Values2);
}
private static bool CompareArrays(int[] values, int[] otherValues)
{
for (var i = 0; i < maxNumberOfRandom; i++)
{
if (values[i] != otherValues[i])
{
return false;
}
}
return true;
}
public override int GetHashCode()
{
unchecked
{
var hashCode = Values1.GetHashCode();
hashCode = (hashCode * 397) ^ Values2.GetHashCode();
return hashCode;
}
}
}
static void Main(string[] args)
{
const int count = 20000;
var list = new List<Data>(count);
// initialization loop to provision the required memory on the heap
for (int i = 0; i < count; i++)
{
list.Add(new Data());
}
while (true)
{
var rng = new Random(1);
Parallel.For(0, 20000, i =>
{
// Random isn't thread-safe: https://docs.microsoft.com/en-us/dotnet/api/system.random?view=netframework-4.7.2#the-systemrandom-class-and-thread-safety
int r;
lock (rng)
{
r = rng.Next(0, 3);
}
if (r == 0)
{
// we can do index-based access here so no need for locking
list[i].IsNull = true;
}
else
{
// we can do index-based access here so no need for locking
var data = list[i];
data.IsNull = false;
int j;
for (j = 0; j < r; j++)
{
data.Values1[j] = j;
data.Values2[j] = j * j;
}
Array.Clear(data.Values1, j, data.Values1.Length - j);
Array.Clear(data.Values2, j, data.Values2.Length - j);
}
});
var sw = Stopwatch.StartNew();
var results = list.ToList().AsParallel().Where(x => !x.IsNull).Distinct().ToList();
var time = sw.ElapsedMilliseconds;
Console.WriteLine($"CB count: {list.Count:00000}, result count: {results.Count:00}, time: {time:000}");
}
}