diff --git a/scripts/features/raspberry_pi_nextcloud.sh b/scripts/features/raspberry_pi_nextcloud.sh index 4c18b8a..772dde2 100755 --- a/scripts/features/raspberry_pi_nextcloud.sh +++ b/scripts/features/raspberry_pi_nextcloud.sh @@ -545,6 +545,7 @@ EOF # Fix Nextcloud Issues # ============================================================================= +# shellcheck disable=SC2120 # Optional arguments are intentional phase_fix_issues() { check_root diff --git a/scripts/utils/analyze_repo.sh b/scripts/utils/analyze_repo.sh index 9dc5f82..e03e447 100755 --- a/scripts/utils/analyze_repo.sh +++ b/scripts/utils/analyze_repo.sh @@ -109,7 +109,7 @@ find_files() { # Not a git repo - fall back to manual exclusion local find_args=() for i in "${!patterns[@]}"; do - if [ $i -eq 0 ]; then + if [ "$i" -eq 0 ]; then find_args+=(-name "${patterns[$i]}") else find_args+=(-o -name "${patterns[$i]}") @@ -121,7 +121,7 @@ find_files() { # No filtering - find all files local find_args=() for i in "${!patterns[@]}"; do - if [ $i -eq 0 ]; then + if [ "$i" -eq 0 ]; then find_args+=(-name "${patterns[$i]}") else find_args+=(-o -name "${patterns[$i]}") @@ -298,7 +298,7 @@ if [ "$RESPECT_GITIGNORE" = true ] && is_git_repo; then } | sort -u | wc -l) echo "Files: $FILE_COUNT (respecting .gitignore)" elif [ "$RESPECT_GITIGNORE" = true ]; then - echo "Files: $(find . -type f 2> /dev/null | grep -Ev "/($EXCLUDE_DIRS)/" | wc -l) (excluding common dirs)" + echo "Files: $(find . -type f 2> /dev/null | grep -cEv "/($EXCLUDE_DIRS)/") (excluding common dirs)" else echo "Files: $(find . -type f | wc -l)" fi @@ -332,7 +332,7 @@ print_header "STEP 4: Fast Keyword Analysis (Code vs Comments)" fast_count() { local top_n="${1:-50}" if command -v counts &> /dev/null; then - counts 2> /dev/null | head -$((top_n + 1)) | tail -$top_n + counts 2> /dev/null | head -$((top_n + 1)) | tail -"$top_n" else python3 -c " import sys @@ -389,14 +389,14 @@ HAS_GO=false HAS_RUST=false HAS_JAVA=false -((${LANG_FILES[c]} + ${LANG_FILES[cpp]} + ${LANG_FILES[h]} > 0)) && HAS_C_FAMILY=true -((${LANG_FILES[python]} > 0)) && HAS_PYTHON=true -((${LANG_FILES[javascript]} + ${LANG_FILES[typescript]} > 0)) && HAS_JS_FAMILY=true -((${LANG_FILES[shell]} > 0)) && HAS_SHELL=true -((${LANG_FILES[ruby]} > 0)) && HAS_RUBY=true -((${LANG_FILES[go]} > 0)) && HAS_GO=true -((${LANG_FILES[rust]} > 0)) && HAS_RUST=true -((${LANG_FILES[java]} > 0)) && HAS_JAVA=true +((LANG_FILES[c] + LANG_FILES[cpp] + LANG_FILES[h] > 0)) && HAS_C_FAMILY=true +((LANG_FILES[python] > 0)) && HAS_PYTHON=true +((LANG_FILES[javascript] + LANG_FILES[typescript] > 0)) && HAS_JS_FAMILY=true +((LANG_FILES[shell] > 0)) && HAS_SHELL=true +((LANG_FILES[ruby] > 0)) && HAS_RUBY=true +((LANG_FILES[go] > 0)) && HAS_GO=true +((LANG_FILES[rust] > 0)) && HAS_RUST=true +((LANG_FILES[java] > 0)) && HAS_JAVA=true #------------------------------------------------------------------------------ # Language-specific keyword definitions @@ -718,7 +718,9 @@ ugrep -o '\b[a-zA-Z_][a-zA-Z0-9_]*\b' "$COMMENTS_TEMP" 2> /dev/null | # List what per-language files were created echo "" echo "Per-language analysis files created:" -ls -la "$RESULTS_DIR/per_language/" 2> /dev/null | grep -v '^total' | awk '{print " " $NF}' +if [ -d "$RESULTS_DIR/per_language/" ]; then + find "$RESULTS_DIR/per_language/" -maxdepth 1 -type f -printf " %f\n" 2> /dev/null | sort +fi print_subheader "Generating tags (this may take a while)..." @@ -737,7 +739,7 @@ if [ -f "$RESULTS_DIR/tags" ]; then print_subheader "Symbol Types Distribution" # Fast: extract single-letter kind code after ;" and count - grep -aoP ';"\t\K[a-z]' "$RESULTS_DIR/tags" 2> /dev/null | fast_count 20 | while read count kind; do + grep -aoP ';"\t\K[a-z]' "$RESULTS_DIR/tags" 2> /dev/null | fast_count 20 | while read -r count kind; do case $kind in f) echo "$count functions" ;; v) echo "$count variables" ;; diff --git a/scripts/utils/android_guardian/service.sh b/scripts/utils/android_guardian/service.sh index 8729326..f58bec8 100755 --- a/scripts/utils/android_guardian/service.sh +++ b/scripts/utils/android_guardian/service.sh @@ -79,7 +79,11 @@ check_blocked_apps() { # Check if package is installed if pm list packages 2> /dev/null | grep -q "package:$package"; then log "Blocked app detected: $package - Uninstalling..." - pm uninstall "$package" 2> /dev/null && log "Uninstalled: $package" || log "Failed to uninstall: $package" + if pm uninstall "$package" 2> /dev/null; then + log "Uninstalled: $package" + else + log "Failed to uninstall: $package" + fi fi done < "$BLOCKED_APPS_FILE" } diff --git a/scripts/utils/download_exercism_bulk.sh b/scripts/utils/download_exercism_bulk.sh index 3ee3112..869ba83 100755 --- a/scripts/utils/download_exercism_bulk.sh +++ b/scripts/utils/download_exercism_bulk.sh @@ -64,7 +64,9 @@ for track in "${!TRACKS[@]}"; do # Show exercise count if [[ -d "$track/exercises/practice" ]]; then - count=$(ls "$track/exercises/practice" | wc -l) + count=$(find "$track/exercises/practice" -maxdepth 1 -type d | wc -l) + # Subtract 1 for the parent directory itself + count=$((count - 1)) echo " → $count practice exercises available" fi echo "" @@ -100,7 +102,9 @@ echo "" echo "Track summary:" for track in "${!TRACKS[@]}"; do if [[ -d "$track/exercises/practice" ]]; then - count=$(ls "$track/exercises/practice" 2> /dev/null | wc -l) + count=$(find "$track/exercises/practice" -maxdepth 1 -type d 2> /dev/null | wc -l) + # Subtract 1 for the parent directory itself + count=$((count - 1)) printf " %-15s %3d exercises\n" "$track" "$count" fi done | sort diff --git a/scripts/utils/generate_study_materials.sh b/scripts/utils/generate_study_materials.sh index 63b3c62..dea352e 100755 --- a/scripts/utils/generate_study_materials.sh +++ b/scripts/utils/generate_study_materials.sh @@ -96,7 +96,6 @@ lookup_offline() { # Python documentation python_doc_url() { local term="$1" - local type="$2" # keyword, builtin, module case "$term" in # Keywords @@ -333,14 +332,14 @@ shell_doc_url() { case "$term" in # Built-in commands - if | then | else | elif | fi | for | while | until | do | done | case | esac | in | function | select | time | coproc) + if | then | else | elif | fi | for | while | until | do | done | case | 'esac' | in | function | select | time | coproc) echo "https://www.gnu.org/software/bash/manual/bash.html#Conditional-Constructs" ;; echo | printf | read | declare | local | export | unset | set | shopt | alias | source | eval | exec | exit | return | break | continue | shift | trap | wait | kill | jobs | bg | fg | disown | suspend | logout | cd | pwd | pushd | popd | dirs | type | which | command | builtin | enable | help | hash | bind | complete | compgen | compopt) echo "https://www.gnu.org/software/bash/manual/bash.html#Shell-Builtin-Commands" ;; # Common external commands - grep | sed | awk | find | xargs | sort | uniq | cut | tr | head | tail | wc | cat | tee | diff | patch | tar | gzip | zip | curl | wget | ssh | scp | rsync | git | make | chmod | chown | chgrp | ln | cp | mv | rm | mkdir | rmdir | touch | ls | stat | file | df | du | free | top | ps | kill | pkill | pgrep | nohup | screen | tmux) + grep | sed | awk | find | xargs | sort | uniq | cut | tr | head | tail | wc | cat | tee | diff | patch | tar | gzip | zip | curl | wget | ssh | scp | rsync | git | make | chmod | chown | chgrp | ln | cp | mv | rm | mkdir | rmdir | touch | ls | stat | file | df | du | free | top | ps | pkill | pgrep | nohup | screen | tmux) echo "https://man7.org/linux/man-pages/man1/$term.1.html" ;; *) @@ -528,7 +527,7 @@ if [ -d "$PER_LANG_DIR" ]; then echo "| Keyword | Count | Documentation |" >> "$DOCS_FILE" echo "|---------|-------|---------------|" >> "$DOCS_FILE" - head -$TOP_N "$keyword_file" | while read -r count term; do + head -"$TOP_N" "$keyword_file" | while read -r count term; do [ -z "$term" ] && continue [[ $term =~ ^[#] ]] && continue # Skip comment lines url=$(get_doc_url "$term" "$doc_lang") @@ -566,7 +565,7 @@ if [ -d "$PER_LANG_DIR" ]; then echo "| Function | Count | Documentation |" >> "$DOCS_FILE" echo "|----------|-------|---------------|" >> "$DOCS_FILE" - head -$TOP_N "$func_file" | while read -r count term; do + head -"$TOP_N" "$func_file" | while read -r count term; do [ -z "$term" ] && continue [[ $term =~ ^(if|for|while|switch|catch|elif)$ ]] && continue url=$(get_doc_url "$term" "$doc_lang") @@ -613,7 +612,7 @@ if [ -d "$PER_LANG_DIR" ]; then module=$(echo "$import" | sed -E 's/.*[<"]([^">]+)[">].*/\1/' | sed 's|.*/||' | sed 's/\..*$//') url=$(get_doc_url "$module" "$doc_lang") fi - import_escaped=$(echo "$import" | sed 's/|/\\|/g') + import_escaped="${import//|/\\|}" echo "| \`$import_escaped\` | $count | [docs]($url) |" >> "$DOCS_FILE" done echo "" >> "$DOCS_FILE" @@ -629,7 +628,7 @@ else echo "| Keyword | Count | Documentation |" >> "$DOCS_FILE" echo "|---------|-------|---------------|" >> "$DOCS_FILE" - head -$TOP_N "$RESULTS_DIR/grep_keywords.txt" | while read -r count term; do + head -"$TOP_N" "$RESULTS_DIR/grep_keywords.txt" | while read -r count term; do [ -z "$term" ] && continue url=$(get_doc_url "$term" "$PRIMARY_LANG") echo "| \`$term\` | $count | [docs]($url) |" >> "$DOCS_FILE" @@ -643,7 +642,7 @@ else echo "| Function | Count | Documentation |" >> "$DOCS_FILE" echo "|----------|-------|---------------|" >> "$DOCS_FILE" - head -$TOP_N "$RESULTS_DIR/grep_function_calls.txt" | while read -r count term; do + head -"$TOP_N" "$RESULTS_DIR/grep_function_calls.txt" | while read -r count term; do [ -z "$term" ] && continue [[ $term =~ ^(if|for|while|switch|catch)$ ]] && continue url=$(get_doc_url "$term" "$PRIMARY_LANG") @@ -662,7 +661,7 @@ else [ -z "$import" ] && continue module=$(echo "$import" | sed -E 's/.*[<"]([^">]+)[">].*/\1/' | sed 's|.*/||' | sed 's/\..*$//') url=$(get_doc_url "$module" "$PRIMARY_LANG") - import_escaped=$(echo "$import" | sed 's/|/\\|/g') + import_escaped="${import//|/\\|}" echo "| \`$import_escaped\` | $count | [docs]($url) |" >> "$DOCS_FILE" done echo "" >> "$DOCS_FILE" @@ -694,7 +693,7 @@ EOF # Generate cards for top keywords if [ -f "$RESULTS_DIR/grep_keywords.txt" ]; then echo "# Keywords" >> "$ANKI_FILE" - head -$TOP_N "$RESULTS_DIR/grep_keywords.txt" | while read -r count term; do + head -"$TOP_N" "$RESULTS_DIR/grep_keywords.txt" | while read -r count term; do [ -z "$term" ] && continue url=$(get_doc_url "$term" "$PRIMARY_LANG") @@ -732,7 +731,7 @@ fi if [ -f "$RESULTS_DIR/grep_function_calls.txt" ]; then echo "" >> "$ANKI_FILE" echo "# Functions" >> "$ANKI_FILE" - head -$TOP_N "$RESULTS_DIR/grep_function_calls.txt" | while read -r count term; do + head -"$TOP_N" "$RESULTS_DIR/grep_function_calls.txt" | while read -r count term; do [ -z "$term" ] && continue [[ $term =~ ^(if|for|while|switch|catch)$ ]] && continue url=$(get_doc_url "$term" "$PRIMARY_LANG") @@ -780,11 +779,14 @@ generate_keywords_with_docs() { local keywords_file="$RESULTS_DIR/grep_keywords.txt" [ ! -f "$keywords_file" ] && echo "No keywords found" && return - head -$TOP_N "$keywords_file" | grep -v '^#' | while read -r line; do - local count=$(echo "$line" | awk '{print $1}') - local keyword=$(echo "$line" | awk '{print $2}') + head -"$TOP_N" "$keywords_file" | grep -v '^#' | while read -r line; do + local count + local keyword + local doc_link + count=$(echo "$line" | awk '{print $1}') + keyword=$(echo "$line" | awk '{print $2}') [ -z "$keyword" ] && continue - local doc_link=$(get_llm_doc_link "$keyword" "$PRIMARY_LANG" "false") + doc_link=$(get_llm_doc_link "$keyword" "$PRIMARY_LANG" "false") echo "$count $keyword → $doc_link" done } @@ -794,16 +796,19 @@ generate_functions_with_docs() { local functions_file="$RESULTS_DIR/grep_function_calls.txt" [ ! -f "$functions_file" ] && echo "No functions found" && return - head -$TOP_N "$functions_file" | grep -v '^#' | while read -r line; do - local count=$(echo "$line" | awk '{print $1}') - local func=$(echo "$line" | awk '{print $2}') + head -"$TOP_N" "$functions_file" | grep -v '^#' | while read -r line; do + local count + local func + local doc_link + count=$(echo "$line" | awk '{print $1}') + func=$(echo "$line" | awk '{print $2}') # Skip single-letter functions (minified code) or empty if [ -z "$func" ] || [ ${#func} -le 1 ]; then continue fi - local doc_link=$(get_llm_doc_link "$func" "$PRIMARY_LANG" "false") + doc_link=$(get_llm_doc_link "$func" "$PRIMARY_LANG" "false") echo "$count $func() → $doc_link" done } @@ -814,15 +819,18 @@ generate_imports_with_docs() { [ ! -f "$imports_file" ] && echo "No imports found" && return head -20 "$imports_file" | grep -v '^#' | while read -r line; do - local count=$(echo "$line" | awk '{print $1}') - local import_stmt=$(echo "$line" | cut -d' ' -f2-) + local count + local import_stmt + local doc_link + count=$(echo "$line" | awk '{print $1}') + import_stmt=$(echo "$line" | cut -d' ' -f2-) [ -z "$import_stmt" ] && continue # Check if internal import if [[ $import_stmt =~ @/ ]] || [[ $import_stmt =~ \'\./ ]] || [[ $import_stmt =~ from\ app\. ]] || [[ $import_stmt =~ from\ src\. ]]; then echo "$count $import_stmt → [INTERNAL - SKIP]" else - local doc_link=$(get_llm_doc_link "$import_stmt" "$PRIMARY_LANG" "true") + doc_link=$(get_llm_doc_link "$import_stmt" "$PRIMARY_LANG" "true") echo "$count $import_stmt → $doc_link" fi done diff --git a/scripts/utils/install_exercism.sh b/scripts/utils/install_exercism.sh index 078a108..4fa2237 100755 --- a/scripts/utils/install_exercism.sh +++ b/scripts/utils/install_exercism.sh @@ -110,7 +110,7 @@ install_exercism_cli() { # Check PATH if [[ ":$PATH:" != *":$HOME/.local/bin:"* ]]; then warn "Add ~/.local/bin to your PATH:" - echo ' export PATH="$HOME/.local/bin:$PATH"' + echo " export PATH=\"\$HOME/.local/bin:\$PATH\"" fi } @@ -151,7 +151,11 @@ install_test_runners() { success "Python: pytest already installed" else info "Installing pytest for Python exercises..." - pip3 install --user pytest 2> /dev/null && success "Python: pytest installed" || warn "Python: install pytest manually" + if pip3 install --user pytest 2> /dev/null; then + success "Python: pytest installed" + else + warn "Python: install pytest manually" + fi fi fi @@ -256,8 +260,8 @@ show_usage() { echo "" echo -e "${CYAN}Batch download (requires API token):${NC}" echo " # Download first 20 Python exercises:" - echo ' for ex in $(exercism download --track=python 2>&1 | head -20); do' - echo ' exercism download --track=python --exercise=$ex' + echo " for ex in \$(exercism download --track=python 2>&1 | head -20); do" + echo " exercism download --track=python --exercise=\$ex" echo " done" echo "" echo "Exercises are in: $EXERCISM_DIR" diff --git a/scripts/utils/install_plagiarism_tools.sh b/scripts/utils/install_plagiarism_tools.sh index 1de0b64..7149803 100755 --- a/scripts/utils/install_plagiarism_tools.sh +++ b/scripts/utils/install_plagiarism_tools.sh @@ -521,12 +521,12 @@ echo " plagcheck thesis_v1.pdf thesis_v2.pdf --detailed" echo " plagcheck --dir ./student_papers/ --threshold 0.4" echo "" echo "Note: Ensure ~/.local/bin is in your PATH:" -echo ' export PATH="$HOME/.local/bin:$PATH"' +echo " export PATH=\"\$HOME/.local/bin:\$PATH\"" echo "" echo "==============================================" # Add to PATH reminder if [[ ":$PATH:" != *":$HOME/.local/bin:"* ]]; then warn "Add ~/.local/bin to your PATH by adding this to ~/.bashrc or ~/.zshrc:" - echo ' export PATH="$HOME/.local/bin:$PATH"' + echo " export PATH=\"\$HOME/.local/bin:\$PATH\"" fi diff --git a/scripts/utils/lookup_docs.sh b/scripts/utils/lookup_docs.sh index 80a2df9..840a22c 100755 --- a/scripts/utils/lookup_docs.sh +++ b/scripts/utils/lookup_docs.sh @@ -887,7 +887,7 @@ main() { if [ "$lang" = "all" ]; then lookup_all "$term" else - result=$(lookup_$lang "$term" 2> /dev/null) + result=$(lookup_"$lang" "$term" 2> /dev/null) if [ -n "$result" ]; then local file desc file=$(echo "$result" | cut -d'|' -f1) diff --git a/scripts/utils/repo_to_study.sh b/scripts/utils/repo_to_study.sh index 85a14ed..d67084c 100755 --- a/scripts/utils/repo_to_study.sh +++ b/scripts/utils/repo_to_study.sh @@ -30,7 +30,6 @@ STUDY_MATERIALS_BASE="$HOME/.local/share/study-materials" # Work directories WORK_DIR="/tmp/repo_study_$$" -OUTPUT_DIR="" # Colors RED='\033[0;31m' diff --git a/scripts/utils/update_android_hosts.sh b/scripts/utils/update_android_hosts.sh index 47f7f6c..9f8861a 100755 --- a/scripts/utils/update_android_hosts.sh +++ b/scripts/utils/update_android_hosts.sh @@ -25,7 +25,10 @@ ensure_adb_installed() { if command -v pacman &> /dev/null; then sudo pacman -S --noconfirm android-tools || die "Failed to install android-tools" elif command -v apt-get &> /dev/null; then - sudo apt-get update && sudo apt-get install -y adb || die "Failed to install adb" + if ! sudo apt-get update; then + die "Failed to update package lists" + fi + sudo apt-get install -y adb || die "Failed to install adb" elif command -v dnf &> /dev/null; then sudo dnf install -y android-tools || die "Failed to install android-tools" else