Wie funktioniert dieses komplexe BASH-Shell-Skript?

Linc Davis, Mitglied der Apple Support Community, hat diesen Befehl entwickelt, um bei der Diagnose von Problemen zu helfen. Die Ausgabe gibt einen allgemeinen Überblick über das eigene System und ist dem Programm etrecheck sehr ähnlich . Dies geschieht jedoch mit einem einzigen Befehl. Ich möchte aufschlüsseln, wie es funktioniert, da ein Großteil der verwendeten Syntax nicht auf den MAN-Seiten zu finden ist. Auch dieses Skript ist Eigentum von Linc Davis. Ich möchte einfach verstehen, wie es ausgeführt wird. Vielen Dank im Voraus.

clear; shopt -s extglob; Fb='%s\n\t(%s)\n'; Fm='\n%s:\n\n%s\n'; Fs='\n%s: %s\n'; PB="/usr/libexec/PlistBuddy -c Print"; Pm () { [[ "$o" ]] && o=$(sed 's/^/ /' <<< "$o") && printf "$Fm" "$1" "$o"; }; Pc () { o=$(egrep -v '^[[:blank:]]*($|#)' "$2"); Pm "$1"; }; Pp () { o=$($PB "$2" | awk -F'= ' \/$3'/{print $2}'); Pm "$1"; }; Ps () { o="${o##+( )}"; [[ "$o" -ne 0 ]] && printf "$Fs" "$1" "$o"; }; a=$(id | grep -w '80(admin)'); r=1; [[ "$a" ]] && { sudo true; r=$?; }; { [[ "$a" ]] || echo $'No admin access\n'; [[ "$a" && "$r" -ne 0 ]] && echo $'No root access\n'; system_profiler SPSoftwareDataType | sed '8!d;s/^ *//'; o=$(system_profiler SPDiagnosticsDataType | sed '5,6!d'); fgrep -q P <<< "$o" && o=; Pm "POST"; o=$(nvram boot-args | awk '{$1=""; print}'); Ps "boot-args"; o=$(df -m / | awk 'NR==2 {print $4}'); [[ $o -lt 5120 ]] && Ps "Free space (MiB)"; o=$(($(vm_stat | awk '/Pageo/{sub("\\.",""); print $2}')/256)); o=$((o>=1024?o:0)); Ps "Pageouts (MiB)"; s=( $(sar -u 1 10 | sed '$!d') ); [[ ${s[4]} -lt 90 ]] && o=$(printf 'User %s%%\t\tSystem %s%%' ${s[1]} ${s[3]}) || o=; Pm "Total CPU usage" && o=$(ps acrx -o comm,ruid,%cpu | sed '2!d'); Pm "Max %CPU by process (name, UID, %)"; o=$(kextstat -kl | grep -v com\\.apple | cut -c53- | cut -d\< -f1); Pm "Loaded extrinsic kernel extensions"; o=$(launchctl list | sed 1d | awk '!/0x|com\.apple|org\.(x|openbsd)|\.[0-9]+$/{print $3}'); Pm "Loaded extrinsic user agents"; o=$(launchctl getenv DYLD_INSERT_LIBRARIES); Pm "Inserted libraries"; for f in crontab fstab launchd.conf sysctl.conf; do Pc $f /etc/$f; done; Pc "hosts" <(sed '1,10d' /etc/hosts); Pc "User crontab" <(crontab -l); Pc "User launchd" ~/.launchd; o=$(find {,/u*/lo*}/e*/periodic -type f -mtime -10d); Pm "Modified periodic scripts"; Pp "Global login items" /L*/P*/loginw* Path; Pp "User login items" L*/P*/*loginit* Name; Pp "Safari extensions" L*/Saf*/*/E*.plist Bundle | sed 's/\..*$//;s/-[1-9]$//'; o=$(find ~ $TMPDIR.. \( -flags +sappnd,schg,uappnd,uchg -o ! -user $UID -o ! -perm -600 \) | wc -l); Ps "Restricted user files"; cd; o=$(find -L /S*/L*/E* {/,}L*/{A*d,Compon,Ex,In,Keyb,Mail/Bu,P*P,Qu,Scripti,Servi,Spo}* -type d -name Contents -prune | while read d; do ID=$($PB\ :CFBundleIdentifier "$d/Info.plist") || ID="No bundle ID"; egrep -qv "^com\.apple\.[^x]|Accusys|ArcMSR|ATTO|HDPro|HighPoint|driver\.stex|hp-fax|\.hpio|JMicron|microsoft\.MDI|print|SoftRAID" <<< $ID && printf "$Fb" "${d%/Contents}" "$ID"; done); Pm "Extrinsic loadable bundles"; o=$(find /u*/{,*/}lib -type f -exec sh -c 'file -b "$1" | grep -qw shared && ! codesign -v "$1"' {} {} \; -print); Pm "Unsigned shared libraries"; o=$(system_profiler SPFontsDataType | egrep "Valid: N|Duplicate: Y" | wc -l); Ps "Font problems"; for d in {/,}L*/{La,Priv,Sta}*; do o=$(ls -A "$d" | egrep -v '\.DS_Store|^com\.apple'); Pm "$d"; done; o=$(ls /L*/L*/Dia*/*.panic | wc -l); Ps "Panics"; o=$(ls /L*/L*/Dia*/*.c* | tail); Pm "System crash logs"; o=$(ls L*/L*/Dia* | tail); Pm "User crash logs"; [[ "$r" -eq 0 ]] && { o=$(sudo profiles -P); Pm "Profiles"; o=$(sudo launchctl list | sed 1d | awk '!/0x|com\.(apple|openssh|vix\.cron)|org\.(amav|apac|calendarse|cups|dove|isc|ntp|post[fg]|x)/{print $3}'); Pm "Loaded extrinsic daemons"; o=$(sudo defaults read com.apple.loginwindow LoginHook); Pm "Login hook"; Pc "Root crontab" <(sudo crontab -l);}; o=$(syslog -F bsd -k Sender kernel -k Message CReq 'GPU |hfs: Ru|I/O e|n Cause: -|NVDA\(|pagin|timed? ?o' | tail -n25 | awk '/:/{$4=""; $5=""; print}'); Pm "Kernel messages"; } 2> /dev/null | pbcopy; exit

Antworten (1)

Das erste, was Sie verstehen müssen, ist, dass es sich nicht wirklich um einen "einzelnen Befehl" handelt, sondern um eine einzelne Zeile, nur weil es ";" verwendet. um Befehlszeilen statt Zeilenumbrüche zu trennen.

Das erste, was Sie tun müssen, wenn Sie versuchen, es zu verstehen, wäre, es für Menschen lesbarer zu machen, indem Sie Ihren bevorzugten Texteditor verwenden, um ';' zu ersetzen. mit einem Zeilenumbruch. Ich habe Textmate verwendet und das Ergebnis als 'linc.sh' gespeichert, damit ich eine Syntaxhervorhebung habe. Dann eine vernünftige Neuformatierung / Verschönerung und ich hatte Folgendes:

Geben Sie hier die Bildbeschreibung ein

Sobald Sie dies getan haben, haben Sie ein Shell-Skript und Sie können sehen, dass es Shell-Variablen und Erweiterungen verwendet, um die Optionen zu erstellen, bevor Sie einen Befehl ausführen, und dann grep, awk und sed verwenden, um die Ausgabe zu verarbeiten und vor dem Wiederholen lesbarer zu machen das Ganze mit einem anderen Befehl. Darin sind auch einige Shell-Funktionen enthalten.

Holen Sie sich ein gutes Buch über die Bash-Programmierung und mit Hilfe dieses Handbuchs und der Bash-Manualpages werden Sie es verstehen.

(Übrigens - dies ist einer der Gründe, warum ich einen Großteil meiner Systemverwaltung in IPython mache, was die Programmierung und den lesbaren Code viel einfacher macht.)

Danke Tony, das sind sehr hilfreiche Informationen. Genauer gesagt, zum Beispiel "Fb/Fm/Fs", wo finde ich Dokumentation dazu? Sind das Variablen? Ich habe kürzlich O'Reilly: "Shell Scripting" gekauft und muss mich noch vollständig damit befassen, plane aber, dies bald zu tun. Danke noch einmal.
Ja, das sind Shell-Variablen. Lesen Sie das O'Reilly-Buch von vorne bis hinten, und ich bin sicher, Sie werden anfangen, alles zu verstehen. Shell-Programmierung auf dieser Ebene ist nicht einfach. Da passiert viel Tiefes.