Как отобразить команды оболочки при их выполнении?

В сценарии оболочки, как я повторяю все вызванные команды оболочки и раскрываю имена переменных? Например, с учетом следующей строки:

ls $DIRNAME

Я хотел бы, чтобы скрипт запускал команду и отображал следующее

ls /full/path/to/some/dir

Цель - сохранить журнал всех вызванных команд оболочки и их аргументов. Возможно, есть лучший способ создания такого журнала?

18 ответов

Решение

set -x или же set -o xtrace раскрывает переменные и печатает маленький знак + перед строкой.

set -v или же set -o verbose не раскрывает переменные перед печатью.

использование set +x а также set +v чтобы отключить вышеуказанные настройки.

В первой строке скрипта можно поставить #!/bin/sh -x (или же -v) иметь тот же эффект, что и set -x (или же -v) позже в сценарии.

Выше также работает с /bin/sh,

http://www.faqs.org/docs/abs/HTML/options.html

$ cat shl
#!/bin/bash                                                                     

DIR=/tmp/so
ls $DIR

$ bash -x shl 
+ DIR=/tmp/so
+ ls /tmp/so
$

set -x даст вам то, что вы хотите.

Вот пример сценария оболочки для демонстрации:

#!/bin/bash
set -x #echo on

ls $PWD

Это расширяет все переменные и печатает полные команды перед выводом команды.

выход:

+ ls /home/user/
file1.txt file2.txt

Я использую функцию, чтобы эхо, а затем запустить команду

#!/bin/bash
#function to display commands
exe() { echo "\$ $@" ; "$@" ; }

exe echo hello world

Какие выводы

$ echo hello world
hello world

Редактировать:

Для более сложных командных каналов и т. Д. Вы можете использовать eval:

#!/bin/bash
#function to display commands
exe() { echo "\$ ${@/eval/}" ; "$@" ; }

exe eval "echo 'Hello World' | cut -d ' ' -f1"

Какие выводы

$  echo 'Hello World' | cut -d ' ' -f1
Hello

Вы также можете переключать это для выбранных строк в вашем скрипте, оборачивая их в set -x а также set +x например

#!/bin/bash
...
if [[ ! -e $OUT_FILE ]];
then
   echo "grabbing $URL"
   set -x
   curl --fail --noproxy $SERV -s -S $URL -o $OUT_FILE
   set +x
fi

Ответ shuckc на вывод выбранных строк имеет несколько недостатков: в итоге вы получите следующее set +x команда также отображается, и вы теряете возможность тестировать код завершения с $? так как он перезаписывается set +x,

Другой вариант - запустить команду в подоболочке:

echo "getting URL..."
( set -x ; curl -s --fail $URL -o $OUTFILE )

if [ $? -eq 0 ] ; then
    echo "curl failed"
    exit 1
fi

который даст вам вывод, как:

getting URL...
+ curl -s --fail http://example.com/missing -o /tmp/example
curl failed

Это, однако, требует дополнительных затрат на создание новой подоболочки для команды.

Согласно Руководству по Bash для начинающих TLDP: Глава 2. Написание и отладка скриптов

2.3.1. Отладка всего скрипта

$ bash -x script1.sh

...

Теперь на SourceForge доступен полноценный отладчик для Bash. Эти функции отладки доступны в большинстве современных версий Bash, начиная с 3.x.

2.3.2. Отладка в части (ях) скрипта

set -x            # activate debugging from here
w
set +x            # stop debugging from here

...

Таблица 2-1. Обзор установленных опций отладки

Short  | Long notation | Result  
-------+---------------+--------------------------------------------------------------
set -f | set -o noglob | Disable file name generation using metacharacters (globbing).  
set -v | set -o verbose| Prints shell input lines as they are read.  
set -x | set -o xtrace | Print command traces before executing command.  

...

В качестве альтернативы, эти режимы могут быть указаны в самом скрипте, добавляя нужные параметры в объявление оболочки первой строки. Опции можно комбинировать, как это обычно бывает с командами UNIX:

#!/bin/bash -xv

Вы можете выполнить скрипт bash в режиме отладки с опцией -x.
Это будет повторять все команды.

bash -x example_script.sh

# Console output
+ cd /home/user
+ mv text.txt mytext.txt


Вы также можете сохранить опцию -x в скрипте. Просто укажите опцию -x в шебанге.

######## example_script.sh ###################
#!/bin/bash -x

cd /home/user
mv text.txt mytext.txt

##############################################

./example_script.sh

# Console output
+ cd /home/user
+ mv text.txt mytext.txt



Другой вариант - поставить "-x" в верхней части скрипта, а не в командной строке:

$ cat ./server
#!/bin/bash -x
ssh user@server

$ ./server
+ ssh user@server
user@server's password: ^C
$

(Недостаточно повторений, чтобы прокомментировать выбранный ответ.)

Объединив все ответы, я нашел, что это лучший

exe(){
    set -x
    "$@"
    { set +x; } 2>/dev/null
}
# example
exe go generate ./...

{ set +x; } 2>/dev/nullиз /questions/8220321/bash-set-x-bez-pechati/8220335#8220335

Введите "bash -x" в командной строке перед именем сценария bash. Например, чтобы выполнить foo.sh, введите:

bash -x foo.sh

Для зш эхо

 setopt VERBOSE

и для отладки

 setopt XTRACE

Кто-то выше написал:

#!/bin/bash
#function to display commands
exe() { echo "\$ $@" ; "$@" ; }

и это выглядит многообещающе, но я не могу на всю жизнь понять, что он делает. Я гуглил и искал на странице man bash "\$" и "$@", и не нашел абсолютно ничего.

Я понимаю, что создается функция с именем "exec()". Я понимаю, что фигурные скобки отмечают начало и конец функции. Думаю, я понимаю, что точка с запятой обозначает "жесткий возврат" между многострочными командами, так что '{ echo "\$ $@"; "$@"; }'становится, по сути:

{
echo "\$ $@"
"$@"

}

Кто-нибудь может дать мне краткое объяснение, или где найти эту информацию, так как, очевидно, мое google-fu меня подводит?

(Не имея смысла начинать новый вопрос со старого потока, моя цель состоит в том, чтобы перенаправить вывод в файл. Метод "set -x; [команды]; set +x" будет работать для меня достаточно хорошо, но я могу " Я не мог понять, как выводить результаты в файл, а не на экран, поэтому я пытался понять этот другой метод в надежде, что смогу использовать очень плохое понимание перенаправления /pipe /tee/etc, чтобы сделать то же самое.)

Спасибо!

ПОСЛЕДНЕЕ РЕДАКТИРОВАНИЕ:

С некоторой переделкой, я думаю, я понял это. Вот мой эквивалентный код для того, что мне нужно:

SCRIPT_LOG="\home\buddy\logfile.txt"
exe () {
  params="$@"                       # Put all of the command-line into "params"
  printf "%s\t$params" "$(date)" >> "$SCRIPT_LOG"   # Print the command to the log file
  $params                           # Execute the command
}

exe rm -rf /Library/LaunchAgents/offendingfile
exe rm -rf /Library/LaunchAgents/secondoffendingfile

Результаты в файле logfile.txt выглядят примерно так:

Tue Jun  7 16:59:57 CDT 2016  rm -rf /Library/LaunchAgents/offendingfile
Tue Jun  7 16:59:57 CDT 2016  rm -rf /Library/LaunchAgents/secondoffendingfile

Как раз то, что мне было нужно. Спасибо!

За csh а также tcsh, вы можете set verbose или же set echo (или вы можете даже установить оба, но это может привести к некоторому дублированию в большинстве случаев).

verbose option выводит в точности точное выражение оболочки, которое вы набираете.

echo Опция более показательна к тому, что будет выполнено через нерест.


http://www.tcsh.org/tcsh.html/Special_shell_variables.html

http://www.tcsh.org/tcsh.html/Special_shell_variables.html


Special shell variables

verbose If set, causes the words of each command to be printed, after history substitution (if any). Set by the -v command line option.

echo If set, each command with its arguments is echoed just before it is executed. For non-builtin commands all expansions occur before echoing. Builtin commands are echoed before command and filename substitution, because these substitutions are then done selectively. Set by the -x command line option.

Чтобы разрешить отображение составных команд, я использую eval плюс Сот exe функцию, чтобы повторить, а затем запустить команду. Это полезно для переданных по конвейеру команд, которые в противном случае показывали бы только одну или только начальную часть переданной по команде команды.

Без оценки

exe() { echo "\$ $@" ; "$@" ; }
exe ls -F | grep *.txt

Выходы:

$
file.txt

С eval:

exe() { echo "\$ $@" ; "$@" ; }
exe eval 'ls -F | grep *.txt'

Какие выводы

$ exe eval 'ls -F | grep *.txt'
file.txt

Я использую следующее, чтобы точно контролировать то, что я хочу видеть:

      #!/bin/bash

cmd="ls $PWD"
echo $cmd; eval $cmd

cmd="cat $PWD/whatfille.txt"
echo "-> $cmd"; eval $cmd      #this prefixes to command for better reading

Ключом к расширению переменной является использование двойной кавычки. Одиночная кавычка не будет расширяться.

Иногда я использую «logger» вместо «echo» для отправки в /var/log/syslog, поскольку они часто запускаются неинтерактивно.

После ответа Сота .

Можно создать вывод уценки без выделения (язык не указан)

      set -x
exe() { echo "\`\$\$  ${@/eval/} \`" ; "$@" ; }

сценарий

      set -x
exe() { echo "\`\$\$  ${@/eval/} \`" ; "$@" ; }
echo
echo -----------------------------------------------------------
echo # Setup
echo Lets take a random keyframe from in.mp4:
echo
exe eval "kfn=20"
echo
echo "kf=\$(ffprobe -v error -select_streams v -show_frames -print_format csv in.mp4 | grep 'frame,video,0,1' | head -$kfn | tail -1 | perl -pe 's|frame,video,0,1,.*?,(.*?),.*|\1|') "
exe eval "kf=$(ffprobe -v error -select_streams v -show_frames -print_format csv in.mp4 | grep 'frame,video,0,1' | head -$kfn | tail -1 | perl -pe 's|frame,video,0,1,.*?,(.*?),.*|\1|') "
echo
echo Lets select keyframe at $kf. Here are the timestamps of the all the frames from in.mp4 around this keyframe.
echo
exe eval "ffprobe -v error -select_streams v -show_frames -print_format csv in.mp4  | perl -pe 's|frame,video,0,(.*?),.*?,(.*?),.*|\2  \1|' | perl -pe 's|(.*?)  1|\1\tKF|' |  perl -pe 's|(.*?)  0|\1|' |grep -A 5 -B 5 --color $kf"
echo
echo Lets compare 2 methods of split: actual losslesscut 3.53.0 and another one
echo

Выход


Давайте возьмем случайный ключевой кадр из in.mp4:

$$ kfn=20

kf=$(ffprobe -v error -select_streams v -show_frames -print_format csv in.mp4 | grep 'frame,video,0,1' | head -20 | Tail -1 | perl -pe 's|frame,video,0 ,1,. ?,(. ?),.*|\1|')

$$ kf=3.803792

Давайте выберем ключевой кадр по адресу 3.803792. Вот временные метки всех кадров из in.mp4 вокруг этого ключевого кадра.

$$ ffprobe -v error -select_streams v -show_frames -print_format csv in.mp4 | perl -pe 's|frame,video,0,(.*?),.*?,(.*?),.*|\2 \1|' | perl -pe 's|(.*?) 1|\1\tKF|' | perl -pe 's|(.*?) 0|\1|' |grep -A 5 -B 5 --color 3.803792

      3.720375
3.737083
3.753750
3.770417
3.787125
**3.803792**   KF
3.820500
3.837167
3.853833
3.870542
3.887208
$ cat exampleScript.sh
#!/bin/bash
name="karthik";
echo $name;

bash -x exampleScript.sh

Вывод следующий:

Если вы хотите регистрировать только определенные команды, а не каждую строку,set -xнесколько сложно использовать . Но вы можете использовать эту функцию для печати любой команды с префиксом$ :

      function run() {
    echo -n '$'  # Print $ without a newline.
    for arg in "$@"; do
      printf " %q" "$arg"  # Print $arg, properly escaped.
    done
    echo  # Print a newline.
    "$@"
}

Использование:

      run ls /full/path/to/some/dir

С использованиемprintfс%qСпецификатор формата гарантирует, что выходные данные будут напечатаны способом, пригодным для копирования и вставки в оболочку, даже при наличии пробелов или других специальных символов. Кажется, он выходит из пробелов, используя\ вместо добавления кавычек, но выходные данные правильно представляют то, что на самом деле выполняется.

Другие вопросы по тегам