For exercise I need to create a script that takes 2 parameters as input,
<n>
an integer value<path>
the path to a directory
I need to create as many directories in path as there are users that have at least <n>
processes running.
The files have this formatting <pid>.txt
and must contain ppid, time and command
#! /bin/bash
if [ $# -lt 2 ]
then
echo "Hai inserito $#/2 parametri"
echo "<n><path>"
exit 1
fi
if [ "$1" -lt 0 ]
then
echo "errore: Il valore inserito non può essere negativo"
exit 1
fi
if ! [ -d "$2" ]
then
echo "errore: Il path indicato non è una directory"
exit 1
fi
OCCURRENCE=$1
PAT=$2
users=($(ps -Af | awk '{print $1}' | sort | uniq ))
for user in "${users[@]}"
do
processes=$(ps -Af | awk -v user="$user" '$1==user'|wc -l)
if [ "$processes" -gt "$OCCURRENCE" ]
then
mkdir $PAT/$user
ps -Af | awk -v user="$user" -v path="$PAT/$user" '$1=="user" {print $2,$7,$8 >>"$path/"$1".txt" }'
fi
done
The problem is that the files with the contents within the respective directories are not created, but the program only correctly creates the directories of the users that have more than <n>
processes.
Is there any error in the awk command? Any better way to perform this task?
CodePudding user response:
Your first awk
filter is right: $1==user
. But the second is wrong: $1=="user"
compares the first field with the literal string user
, not with the value of variable user
. As noted in comments you have a similar problem with "$path"
that should be path
(no $
, no quotes).
Note also that you should filter out the first line of ps
output, and that, different from bash redirections, >
concatenates, no need for >>
.
As you use awk
here is another solution entirely based on awk
(tested with GNU awk
):
$ ps -Af | awk -v n="$1" -v p="$2" 'NR>1 {
num[$1] = 1
proc[$1][$2 " " $7 " " $8]
}
END {
for(u in num) {
if(num[u] >= n) {
d = p "/" u
system("mkdir -p " d)
for(e in proc[u])
print e > d "/" u ".txt"
}
}
}'
CodePudding user response:
This Shellcheck-clean Bash code demonstrates another way to perform the task:
#! /bin/bash -p
if (( $# < 2 )); then
echo "Hai inserito $#/2 parametri" >&2
echo "<n><path>" >&2
exit 1
fi
min_proc_count=$1
output_dir=$2
if [[ -z $min_proc_count || $min_proc_count == *[^[:digit:]]* ]]; then
echo 'errore: il valore inserito deve essere un numero positivo' >&2
exit 1
fi
if [[ ! -d $output_dir ]]; then
echo 'errore: Il path indicato non è una directory' >&2
exit 1
fi
ps_output=$(ps -Af --no-headers)
# Get counts of processes for each user
declare -A user_proc_count
while read -r user _; do
user_proc_count[$user]=$(( ${user_proc_count[$user]-0} 1 ))
done <<<"$ps_output"
# Generate per-process output files for users with enough processes
while read -r user pid ppid _ _ _ time cmd; do
(( ${user_proc_count[$user]} < min_proc_count )) && continue
dir=$output_dir/$user
[[ -d $dir ]] || mkdir -v -- "$dir"
printf '%d,%s,%s\n' "$ppid" "$time" "$cmd" >"$dir/${pid}.txt"
done <<<"$ps_output"
- See Correct Bash and shell script variable capitalization for an explanation of why I used lowercase variable names instead of ALL_UPPERCASE names like
PAT
andOCCURRENCE
. - See the accepted, and excellent, answer to Why is printf better than echo? for an explanation of why I used
printf
instead ofecho
for outputting data.
CodePudding user response:
Here's an awk
-based solution that doesn't require any additional shell-level processing, and directly generate the necessary outputs.
Based on my amateur-hour understanding of ps, I think ps -Ao 'uid pid time command'
may serve his/her purposes better.
For reasons truly unknown to me, i only could get it to work on gawk
or mawk2
, but not mawk-1
or nawk
.
CODE
ps -Ao 'uid pid time command' |
gawk -be '
{
528 ___[_=$!-__]
528 sub(_ "[ \t] ","")
528 sub("$",($-__)__,___[_])
} END {
1 sub("^[~][/]",ENVIRON["HOME"]"/",____)
gsub("[/] ", "/",____)
sub("[/]$", "",____)
gsub(/\47/,"&\\&&",____)
1 system(" mkdir -p \47" (____) "\47 2>/dev/null")
35 for (_ in ___) {
35 if ( _____ < (gsub(__, "&", ___[_]))) {
5 printf("%s", ___[_]) > (____ "/" _".txt")
}
}
}' _____='THRESHOLD_NUMBER' ____="${DESTINATION_PATH}" __='\n'
"${DESTINATION_PATH}" afterwards
total 800
-rw-r--r-- 1 501 staff 11709 May 30 11:13 0.txt
-rw-r--r-- 1 501 staff 509 May 30 11:13 205.txt
-rw-r--r-- 1 501 staff 655 May 30 11:13 262.txt
-rw-r--r-- 1 501 staff 1575 May 30 11:13 278.txt
-rw-r--r-- 1 501 staff 791516 May 30 11:13 501.txt
296 lines 791,175 utf8 (175 uc) 0.755 MB ( 791515) 501.txt
158 lines 11,708 utf8 (0. uc) 0.011 MB ( 11708) 0.txt
6 lines 508 utf8 (0. uc) 0.000 MB ( 508) 205.txt
12 lines 1,574 utf8 (0. uc) 0.002 MB ( 1574) 278.txt
7 lines 654 utf8 (0. uc) 0.001 MB ( 654) 262.txt
==> 0.txt <==
1 4:36.92 /sbin/launchd
105 1:00.85 /usr/libexec/logd
==> 205.txt <==
148 0:32.10 /usr/libexec/locationd
286 0:00.56 /System/Library/PrivateFrameworks/GeoServices.framewor
==> 262.txt <==
346 0:00.84 /usr/sbin/distnoted agent
365 0:00.06 /System/Library/Frameworks/CoreMediaIO.framework/Versi
==> 278.txt <==
113 0:00.46 /System/Library/PrivateFrameworks/MobileAccessoryUpdat
376 0:00.83 /usr/sbin/distnoted agent
==> 501.txt <==
184 0:01.64 /System/Library/CoreServices/loginwindow.app/Contents/
426 0:22.44 /usr/sbin/distnoted agent