ฉันจะพิมพ์เฉพาะคำสั่งบางอย่างจากสคริปต์ทุบตีขณะที่ทำงานได้อย่างไร


19

ฉันมีสคริปต์ทุบตีด้วยถ้าคำสั่งต่างๆตามอาร์กิวเมนต์บรรทัดคำสั่งที่ฉันผ่านเมื่อเรียกมัน การมีเอาต์พุตบางอย่างเกี่ยวกับคำสั่งที่กำลังรันอยู่มีประโยชน์ในการยืนยันการไหลของข้อมูลทั้งหมดหากข้อความสั่ง แต่โซลูชันปัจจุบันของฉันให้ข้อมูลมากเกินไป

การใช้set -vในสคริปต์ค่อนข้างมีประโยชน์ในการดูคำสั่งที่พิมพ์บนหน้าจอขณะที่รันในสคริปต์ แต่ฉันได้รับคำสั่งมากเกินไป มันเกือบจะเหมือนสำเนาทั้งหมดของสคริปต์

ฉันต้องการเอาต์พุตที่แสดงว่าคำสั่งใดกำลังทำงานอยู่ แต่ฉันไม่ต้องการเห็นข้อคิดเห็นบรรทัดใหม่นิพจน์ใน if statement ฯลฯ

มีวิธีที่ฉันสามารถส่งออกเป็นไปได้ทั้งหมดที่สร้างขึ้นโดยตัวเลือก -v ผ่าน regex ก่อนที่จะถูกพิมพ์? หรือวิธีอื่น ๆ ในการรับ bash ไปยังคำสั่งเอาต์พุตของ "type" บางอย่างเท่านั้น (เช่นที่กำลังใช้ executables และไม่เพียงแค่ bash statement เฉพาะข้อคิดเห็น ฯลฯ หรือไม่)

[1] /programming/257616/sudo-changes-path-whyมีประโยชน์มากในเรื่องนี้และเป็นที่ที่ฉันได้รับคำแนะนำสำหรับการset -vใช้งาน

แก้ไข :

สคริปต์ที่คล้ายกัน (แต่ไม่เหมือนกัน) กับสคริปต์ที่ฉันใช้:

#!/bin/bash

#get verbose command output
set -v

env=$1

if [ "$env" == "dev" ]; then
    python ascript.py
fi

if [ "$env" == "prod" ]; then

    #launching in prod will most likely fail if not run as root. Warn user if not running as root.
    if [ $EUID -ne 0 ]; then
        echo "It doesn't look like you're running me as root. This probably won't work. Press any key to continue." > /dev/stderr
        read input
    fi

    #"stop" any existing nginx processes
    pkill -f nginx

    nginx -c `pwd`/conf/artfndr_nginx.conf

fi

ฉันต้องการชุดเอาต์พุตบรรทัดที่เป็นไปได้เพียง 2 ชุดจากสคริปต์นี้ ครั้งแรก:

python ascript.py

ที่สอง:

pkill -f nginx
nginx -c /some/current/directory/conf/artfndr_nginx.conf

1
แน่นอนคุณสามารถแยกวิเคราะห์ได้ แต่เราไม่สามารถช่วยได้เว้นแต่คุณจะแสดงสคริปต์ให้เราและอธิบายว่าset -vคุณต้องการส่วนใดของผลลัพธ์และส่วนที่คุณไม่ต้องการ
terdon

คำตอบ:


12

เมื่อฉันเขียนสคริปต์ทุบตีที่ซับซ้อนมากขึ้นฉันใช้ฟังก์ชั่นเล็ก ๆ เพื่อเรียกใช้คำสั่งที่จะพิมพ์คำสั่งที่เรียกใช้ลงใน logfile:

runthis(){
    ## print the command to the logfile
    echo "$@" >> $LOG
    ## run the command and redirect it's error output
    ## to the logfile
    eval "$@" 2>> $LOG 
}

จากนั้นในสคริปต์ของฉันฉันรันคำสั่งดังนี้:

runthis "cp /foo/bar /baz/"

หากคุณไม่ต้องการให้พิมพ์คำสั่งให้รันตามปกติ

คุณสามารถตั้งค่า$LOGเป็นชื่อไฟล์หรือเพียงแค่ลบมันและพิมพ์ไปยัง stdout หรือ stderr


1 นอกจากนี้ผมก็สามารถที่จะทำงานนี้ภายในสคริปต์ของฉันโดยเพียงแค่ prepending คำสั่ง "สำคัญ" กับรุ่นสั้นที่มีชื่อของฟังก์ชั่นเพื่อให้เส้นที่มีลักษณะบางอย่างเช่นv python ascript.pyได้โดยไม่ต้องใส่ในการเสนอราคาและสูญเสียรหัสที่เป็นกลุ่มของฉันไฮไลท์
Trindaz

@Trindaz คำพูดจะอยู่ที่นั่นเมื่อคุณต้องการส่งผ่านตัวแปรในคำสั่งของคุณหากตัวแปรมีช่องว่างคุณอาจมีปัญหาเป็นอย่างอื่น
terdon

eval ..... || ok=1: จะตั้งค่าตกลงเป็น "1" เฉพาะเมื่อ "eval ... " ล้มเหลว ?? บางทีคุณอาจหมายถึง "&&"? และถ้าคุณหมายถึงให้เพิ่ม "ok = 0" ก่อนหน้าบรรทัด eval ดังนั้นจึงเป็น "รีเซ็ต" ทุกครั้ง หรือเพียงแค่เปลี่ยนชื่อ "ตกลง" เป็น "ข้อผิดพลาด" ดูเหมือนว่าเป็นสิ่งที่มีความหมายที่นี่ ดังนั้นในที่สุด:eval "$@" 2>> "$LOG" && error=0 || error=1
Olivier Dulac

@OlivierDulac ในเวอร์ชันนี้ฉันใช้ฉันมีokตัวแปรที่จะหยุดสคริปต์หากคำสั่งใด ๆ ล้มเหลว ตั้งแต่ที่ไม่เกี่ยวข้องที่นี่ผมหยิบมันออกมา || ok=1แต่ลืมที่จะลบ ขอบคุณคงตอนนี้
terdon

ทางออกที่ดีเยี่ยม! ฉันต้องลบ"คำแถลง eval รอบ ๆ เพราะคำสั่งนั้นถูกล้อมรอบด้วย"s
gromit190

11

ใช้ sub-shell เช่น:

( set -x; cmd1; cmd2 )

ตัวอย่างเช่น:

( set -x; echo "hi there" )

พิมพ์

+ echo 'hi there'
hi there

ฉันชอบอันนี้มากกว่าset -x; cmd; set +xด้วยเหตุผลหลายประการ ก่อนอื่นจะไม่รีเซ็ตset -xในกรณีที่เปิดไว้ก่อนหน้านี้ ประการที่สองการยกเลิกสคริปต์ภายในไม่ก่อให้เกิดกับดักโดยการตั้งค่า verbose
Oliver Gondža

2

ฉันเห็นวิธีการใช้คล้ายกับ @ terdon's มันเป็นจุดเริ่มต้นของภาษาการเขียนโปรแกรมระดับสูงที่เรียกตัวบันทึกการโทรและเสนอเป็นไลบรารี่แบบเต็มเช่น log4J (Java), log4Perl (Perl) เป็นต้น

คุณสามารถใช้สิ่งที่คล้ายกันset -xใน Bash ตามที่คุณกล่าวถึง แต่คุณสามารถใช้มันเพื่อเปิดใช้งานการดีบักเพียงชุดย่อยของคำสั่งโดยการตัดบล็อคของรหัสกับพวกเขาเช่นนั้น

$ set -x; cmd1; cmd2; set +x

ตัวอย่าง

นี่คือรูปแบบซับที่คุณสามารถใช้ได้

$ set -x; echo  "hi" ;set +x
+ echo hi
hi
+ set +x

คุณสามารถใส่คำสั่งนี้ในหลาย ๆ คำสั่งในสคริปต์

set -x
cmd1
cmd2
set +x

cmd3

Log4Bash

คนส่วนใหญ่จะนึกถึง แต่ทุบตียังมี log4 * เช่นกันLog4Bash หากคุณมีความต้องการที่ถ่อมตัวมากขึ้นสิ่งนี้อาจคุ้มค่ากับเวลาในการตั้งค่า

log4bash เป็นความพยายามที่จะมีการบันทึกที่ดีขึ้นสำหรับสคริปต์ Bash (เช่นทำการเข้าสู่ระบบใน Bash suck น้อยลง)

ตัวอย่าง

นี่คือตัวอย่างของการใช้ log4bash

#!/usr/bin/env bash
source log4bash.sh

log "This is regular log message... log and log_info do the same thing";

log_warning "Luke ... you turned off your targeting computer";
log_info "I have you now!";
log_success "You're all clear kid, now let's blow this thing and go home.";
log_error "One thing's for sure, we're all gonna be a lot thinner.";

# If you have figlet installed -- you'll see some big letters on the screen!
log_captains "What was in the captain's toilet?";

# If you have the "say" command (e.g. on a Mac)
log_speak "Resistance is futile";

Log4sh

หากคุณต้องการสิ่งที่ฉันจะจัดเป็นกรอบเต็มกำลังมากกว่า log4 * แล้วฉันจะลองLog4sh

สิ่งที่สกัดมา

เดิมที log4sh ได้รับการพัฒนาเพื่อแก้ไขปัญหาการบันทึกที่ฉันมีในสภาพแวดล้อมการผลิตบางอย่างที่ฉันได้ทำงานในที่ที่ฉันมีการบันทึกมากเกินไปหรือไม่เพียงพอ โดยเฉพาะอย่างยิ่งงานของ Cron ทำให้ฉันปวดหัวที่สุดกับอีเมลที่คงที่และน่ารำคาญของพวกเขาบอกฉันว่าทุกอย่างทำงานได้หรือไม่มีอะไรที่ทำงานได้ แต่ไม่ใช่เหตุผลโดยละเอียดว่าทำไม ตอนนี้ฉันใช้ log4sh ในสภาพแวดล้อมที่การบันทึกจากเชลล์สคริปต์มีความสำคัญ แต่ที่ฉันต้องการมากกว่า "Hello, fix me!" ประเภทข้อความบันทึก หากคุณชอบสิ่งที่คุณเห็นหรือมีข้อเสนอแนะในการปรับปรุงโปรดส่งอีเมลถึงฉัน หากมีความสนใจเพียงพอในโครงการฉันจะพัฒนาต่อไป

log4sh ได้รับการพัฒนาภายใต้ Bourne Again Shell (/ bin / bash) บน Linux แต่ได้รับการดูแลอย่างดีเพื่อให้แน่ใจว่าทำงานภายใต้ Bourne Shell ของ Solaris (/ bin / sh) ที่เป็นค่าเริ่มต้นเนื่องจากนี่เป็นการผลิตหลัก แพลตฟอร์มที่ใช้ด้วยตัวเอง

Log4sh รองรับเชลล์หลายตัวไม่ใช่แค่ Bash

  • บอร์นเชลล์ (SH)
  • BASH - GNU Bourne SHell อีกครั้ง (ทุบตี)
  • DASH (เส้นประ)
  • Korn Shell (ksh)
  • pdksh - Public Domain Korn Shell (pdksh)

มันยังได้รับการทดสอบในหลาย ๆ ระบบปฏิบัติการไม่ใช่แค่ลินุกซ์

  • Cygwin (ภายใต้ Windows)
  • FreeBSD (รองรับผู้ใช้)
  • Linux (Gentoo, RedHat, Ubuntu)
  • Mac OS X
  • โซลาริส 8, 9, 10

การใช้เฟรมเวิร์ก log4 * จะใช้เวลาในการเรียนรู้ แต่ก็คุ้มค่าถ้าคุณมีความต้องการที่มากขึ้นจากการบันทึกของคุณ Log4sh ใช้ประโยชน์จากไฟล์กำหนดค่าที่คุณสามารถกำหนด appenders และควบคุมการจัดรูปแบบสำหรับเอาต์พุตที่จะปรากฏ

ตัวอย่าง

#! /bin/sh
#
# log4sh example: Hello, world
#

# load log4sh (disabling properties file warning) and clear the default
# configuration
LOG4SH_CONFIGURATION='none' . ./log4sh
log4sh_resetConfiguration

# set the global logging level to INFO
logger_setLevel INFO

# add and configure a FileAppender that outputs to STDERR, and activate the
# configuration
logger_addAppender stderr
appender_setType stderr FileAppender
appender_file_setFile stderr STDERR
appender_activateOptions stderr

# say Hello to the world
logger_info 'Hello, world'

ตอนนี้เมื่อฉันเรียกใช้:

$ ./log4sh.bash 
INFO - Hello, world

หมายเหตุ:ข้างต้นกำหนดค่า appender เป็นส่วนหนึ่งของรหัส ถ้าคุณชอบสิ่งนี้สามารถแยกออกเป็นไฟล์ของตัวเองlog4sh.propertiesฯลฯ

ศึกษาเอกสารที่ยอดเยี่ยมสำหรับ Log4shหากคุณต้องการรายละเอียดเพิ่มเติม


ขอบคุณสำหรับบันทึกย่อที่เพิ่มขึ้น แต่ปัญหาหลักที่ฉันมีคือsetคำสั่งทั้งหมดที่ฉันต้องการจะแนะนำสลับรอบความคิดเห็น ฯลฯ ดังนั้นเพียงแค่มีฟังก์ชั่นที่ด้านบนสุดของสคริปต์ของฉัน บรรทัด "สำคัญ" ทั้งหมดในสคริปต์ดูเหมือนจะทำให้ฉันดูตอนนี้ (ตัวอักษรเดียวเพราะฟังก์ชั่นมีชื่อตัวละครตัวเดียว)
Trindaz

@Trindaz - ขอโทษที่ฉันยังตอบไม่เสร็จ ลองดูที่ log4bash หากคุณมีความต้องการมากกว่าที่ฟังก์ชั่นที่ terdon มอบให้
slm

1
@Trindaz - ฉันทำบางสิ่งบางอย่างที่คล้ายกันเป็นครั้งคราววิธีการอื่นที่ฉันใช้คือการห่อechoในฟังก์ชั่นของตัวเองmechoแล้วส่งสวิตช์ไปยังโปรแกรมที่เรียกว่า-vverbose เมื่อฉันต้องการปิดสิ่งต่าง ๆ ฉันสามารถควบคุมมันได้ด้วยสวิตช์ตัวที่ 2 ซึ่งระบุชื่อของฟังก์ชั่นดังนั้นฉันจึงมี 2 แกนที่จะควบคุมการบันทึก นี่เป็นประตูสู่ความต้องการของ log4bash
slm

1
@Trindaz set -xพิมพ์คำสั่งขณะที่ดำเนินการ ไม่พิมพ์ความคิดเห็น set -xเป็นประโยชน์สำหรับการแก้ไขข้อบกพร่อง ( set -vซึ่งแตกต่างจากที่ไม่มีประโยชน์มาก) Zsh มีเอาต์พุตที่ดีกว่าสำหรับset -xbash ตัวอย่างเช่นมันแสดงให้เห็นว่าฟังก์ชั่นใดที่กำลังดำเนินการอยู่และหมายเลขบรรทัดของซอร์ส
Gilles 'หยุดความชั่วร้าย'

ขอบคุณ @Gilles ที่เป็นจริง แต่มันทำให้ฉันถ้าการขยายการแสดงออกซึ่ง overkill ในกรณีนี้
Trindaz

1

คุณสามารถtrap DEBUGแล้วทดสอบตัวแปร เพิ่มสิ่งนี้ไปที่ด้านบนของสคริปต์:BASH_COMMAND

log() {
    case "$1" in
        python\ *)
            ;&
        pkill\ *)
            printf "%s\n" "$*"
            ;;
    esac
}

trap 'log "$BASH_COMMAND"' DEBUG

รหัสสามารถอ่านได้; มันแค่ทดสอบว่าอาร์กิวเมนต์แรกเริ่มต้นด้วยpythonหรือpkillพิมพ์และถ้ามันเป็นกรณี และกับดักใช้BASH_COMMAND(ซึ่งมีคำสั่งที่จะดำเนินการ) เป็นอาร์กิวเมนต์แรก

$ bash foo.sh dev
python ascript.py
python: can't open file 'ascript.py': [Errno 2] No such file or directory
$ bash foo.sh prod
It doesn't look like you're running me as root. This probably won't work. Press any key to continue.

pkill -f nginx
foo.sh: line 32: nginx: command not found

โปรดทราบว่าในขณะที่caseใช้ globs คุณสามารถทำได้อย่างง่ายดาย:

if [[ $1 =~ python|nginx ]]
then
    printf "%s" "$*"
fi

และใช้การแสดงออกปกติ


0

นี่เป็นฟังก์ชั่นที่เรียบร้อยของ Steven Penny รุ่นปรับปรุง มันพิมพ์ข้อโต้แย้งของสีและพูดพวกเขาตามความจำเป็น ใช้มันเพื่อเลือก echo คำสั่งที่คุณต้องการติดตาม เนื่องจากอัญประกาศเป็นผลลัพธ์คุณสามารถคัดลอกบรรทัดที่พิมพ์แล้ววางลงในเทอร์มินัลเพื่อดำเนินการใหม่ทันทีในขณะที่คุณกำลังดีบักสคริปต์ อ่านความคิดเห็นแรกเพื่อทราบสิ่งที่ฉันเปลี่ยนไปและทำไม

xc() # $@-args
{
  cecho "$@"
  "$@"
}
cecho() # $@-args
{
  awk '
  BEGIN {
    x = "\047"
    printf "\033[36m"
    while (++i < ARGC) {
      if (! (y = split(ARGV[i], z, x))) {
        printf (x x)
      } else {
        for (j = 1; j <= y; j++) {
          printf "%s", z[j] ~ /[^[:alnum:]%+,./:=@_-]/ ? (x z[j] x) : z[j]
          if (j < y) printf "\\" x
        }
      }
      printf i == ARGC - 1 ? "\033[m\n" : FS
    }
  }
  ' "$@"
}

ตัวอย่างการใช้งานด้วยเอาต์พุต:

# xc echo "a b" "c'd" "'" '"' "fg" '' " " "" \# this line prints in green

echo 'a b' c\'d \' '"' fg '' ' ' '' '#' this line prints in green

a b c'd ' " fg # this line prints in green

บรรทัดที่สองด้านบนพิมพ์เป็นสีเขียวและสามารถคัดลอกวางเพื่อทำซ้ำบรรทัดที่สาม

ข้อสังเกตเพิ่มเติม

xc ดั้งเดิมของ @ Steven-Penny นั้นฉลาดและเขาสมควรได้รับเครดิตทั้งหมดสำหรับมัน อย่างไรก็ตามฉันสังเกตเห็นปัญหาบางอย่าง แต่ฉันไม่สามารถแสดงความคิดเห็นโพสต์ของเขาโดยตรงเพราะฉันมีชื่อเสียงไม่เพียงพอ ดังนั้นฉันจึงทำการแก้ไขที่แนะนำในโพสต์ของเขา แต่ผู้ตรวจสอบปฏิเสธการแก้ไขของฉัน ดังนั้นฉันจึงหันไปโพสต์ความคิดเห็นของฉันเป็นคำตอบนี้ถึงแม้ว่าฉันต้องการที่จะแก้ไขคำตอบของ Steve Penny

สิ่งที่ฉันเปลี่ยนคำตอบของ wrt Steven-Penny

คงที่: การพิมพ์สตริงโมฆะ - พวกเขาไม่ได้พิมพ์ Fixed: การพิมพ์สตริงที่มี%- พวกเขาทำให้เกิดข้อผิดพลาดทางไวยากรณ์ awk แทนที่for (j in ...)ด้วยfor (j = 0, ...)เพราะอดีตไม่รับประกันลำดับของการแวะผ่านอาร์เรย์ (ขึ้นอยู่กับการใช้งานของ awk) เพิ่ม 0 ถึงหมายเลขฐานแปดเพื่อความสะดวกในการพกพา

ปรับปรุง

Steven Penny's ได้แก้ไขปัญหาเหล่านั้นในคำตอบของเขาแล้วดังนั้นคำพูดเหล่านี้จะยังคงอยู่สำหรับบันทึกทางประวัติศาสตร์ของคำตอบของฉันเท่านั้น ดูส่วนความเห็นสำหรับรายละเอียดเพิ่มเติม


0

คุณสามารถใช้ฟังก์ชันเชลล์ "sh_trace" จากไลบรารี POSIX stdlibเพื่อพิมพ์คำสั่งเป็นสีก่อนเรียกใช้ ตัวอย่าง:

ภาพตัวอย่าง

ฟังก์ชั่นพื้นฐาน Awk:

function sh_trace(ary,   b, d, k, q, w, z) {
  b = "\47"
  for (d in ary) {
    k = split(ary[d], q, b)
    q[1]
    if (d - 1)
      z = z " "
    for (w in q) {
      z = z (!k || q[w] ~ "[^[:alnum:]%+,./:=@_-]" ? b q[w] b : q[w]) \
      (w < k ? "\\" b : "")
    }
  }
  printf "\33[36m%s\33[m\n", z
  system(z)
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.