Как отобразить команды оболочки при их выполнении?
В сценарии оболочки, как я повторяю все вызванные команды оболочки и раскрываю имена переменных? Например, с учетом следующей строки:
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
Кто-то выше написал:
#!/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
Спецификатор формата гарантирует, что выходные данные будут напечатаны способом, пригодным для копирования и вставки в оболочку, даже при наличии пробелов или других специальных символов. Кажется, он выходит из пробелов, используя\
вместо добавления кавычек, но выходные данные правильно представляют то, что на самом деле выполняется.