Дата и время публикации:
1. Назначение
Документ описывает использование статического анализатора кода shellcheck для скриптов сценария BASH, чтобы определить какие ошибки синтаксиса были допущены. Считаю очень полезным не только для улучшения качества кода, но и оптимизации в целях повышения быстродействия скрипта.
Использование shellcheck привожу на примере, разрабатываемого мной проекта "VHD enviroment", позволяющего запускать виртуальные диски на внешних или внутренних системах хранения данных, включая на разделы файловой системы fuseblk(ntfs, extfat и т.п.). В результате проверки скриптов были выявлены следующие ошибки:
Код ошибки | Описание ошибки | Рекомендация |
---|---|---|
SC1068 | Недопускается наличие символов вокруг опрератора присвоения значения переменной (=) | Удалить все символы вокруг оператора присвоения |
SC1078 | Значение строковой переменной не было закрыто второй двойной кавычкой | закрыть двойной кавычкой |
SC1087 | Использование скобок при расширения массива | Если ошибка произошла в регулярном выражении привести декларацию к требуемой |
SC1090 | Невозможно проследовать по указанному пути для source. | Указать директиву # shellcheck source=/dev/null |
SC1115 | Наличие пробелов между # и ! в шебанге | удалить пробелы |
SC2001 | Не рекомендуется использовать sed для замены | Рекомендуется взамен использовать ${variable//search/replace} |
SC2004 | Ненужное использование $/${} на арифметических операциях | Не использовать $/${} в арифметическом выражении $((..)) |
SC2012 | Рекомендуется использовать find или указать полный путь ls | с помощью команды $(which ls) получить полный путь |
SC2025 | Наложение букв вводимой команды на ранее введенную в командной строке при использовании ESC-последовательности | |
SC2034 | Переменная не используется | Проверить фактический код, а в случае использования в других скриптах использовать export |
SC2046 | Заключить в кавычки для превращения дробления слов | см. рекомендацию к SC2086 |
SC2050 | Использование имени переменной в виде константы | перед именем переменной поставить $ |
SC2053 | Значение переменной может быть скомкан, когда стоит справа от оператор сравнение =, == или != | Обрамить кавычками |
SC2070 | -n не работает с не обрамленными кавычками (может сопровождаться SC2086) | заключить в двойные кавычки или использовать ```[[ ]]` `` |
SC2086 | Значение переменной не окаймлено кавычками | Рекомендуется окаймлять двойными кавычками строковое значение во избежание подстановки или разбиения |
SC2116 | Бессмысленное использование команды echo | Взамен i'cmd $(echo foo)' следует использовать 'cmd foo' |
SC2115 | Предотвращение несанкционированного удаления от корня файловой системы (rootfs) при использовании команды rm | использовать :? в rm -rf "${STEAMROOT:?}/"* , чтобы предотвратить удаление от корня rootfs командой rm, когда STEAMROOT не будет присвоено значение |
SC2120 | Отсутствует передача аргументов функции | Передать "@" |
SC2140 | Нельзя применять многократно двойные ковычки в форме "A"B"C" |
Следует использовать форму записи "A\"B\"C" |
SC2141 | Отсутствует буквальный символ 'n' в присваиваемом значении | Использовать специальный $..., алфавитно-буквенный символ или printf |
SC2143 | Повысить эффективность за счет сокращения количества итераций поиска с помощью grep до первого совпадения | Использовать ! grep -q взамен сравнения вывода с [ -z .. ] |
SC2148 | Не указана целевая оболочка | Указать шебанг #!/bin/bash в первой строке скрипта |
SC2152 | Возвращаемое значение функции должно быть в диапазоне от 0,...,255 | Указать числовое значение от 0 до 255 |
SC2154 | Ссылка не существующую на переменную, которая не была назначена | Удалить или исправить имя переменной к существующей |
SC2155 | Требуется разделить декларацию и присвоение значения во избежание маскирования возвращаемого значения | |
SC2162 | Выключить функцию экранирования специальных символов (backslash function) команды read | Использовать read -r -p |
SC2164 | Осутствие проверки результата выполнения команды cd | Использовать 'cd ... |
SC2181 | Не рекомендуется проверять результат выполнения команды mycmd с использованием $? | в условном выражении указывать непосредственно if mycmd ; |
SC2209 | Нельзя присвоить команду,например, сформированную eval, без кавычек или var=$(command) | Применить кавычки или var=$(command) |
SC2236 | Не рекомендуется использовать оператор НЕ с -z | Вместо "! -z" cледует использвоать "-n" |
2. Использование
2.1 Запуск анализатора
$...hdv-env$ shellcheck -s bash bin/vhdenv-scripts
Опция -s или --shell указывает какой тип оболочки используется.
Например, вывод результатов проверки файла bin/vhdbash-newinst-exec до внесения обновлений:
In bin/vhdbash-newinst-exec line 7:
echo "${vhd_bold}VHD enviroment${vhd_normal}: replaced bash instance to be used $VHDENV_BASHRCFILE file"
^---------^ SC2154: vhd_bold is referenced but not assigned.
^-----------^ SC2154: vhd_normal is referenced but not assigned.
In bin/vhdbash-newinst-exec line 8:
exec env -i HOME=$(pwd) TERM=$TERM /bin/bash --rcfile $VHDENV_BASHRCFILE
^----^ SC2046: Quote this to prevent word splitting.
^---^ SC2086: Double quote to prevent globbing and word splitting.
^----------------^ SC2086: Double quote to prevent globbing and word splitting.
Did you mean:
exec env -i HOME=$(pwd) TERM="$TERM" /bin/bash --rcfile "$VHDENV_BASHRCFILE"
For more information:
https://www.shellcheck.net/wiki/SC2046 -- Quote this to prevent word splitt.
https://www.shellcheck.net/wiki/SC2154 -- vhd_bold is referenced but not as...
https://www.shellcheck.net/wiki/SC2086 -- Double quote to prevent globbing ...
Каждый отчет содержит детальное описание всех допущенных ошибок, окрашенные в красный, желтый и зеленый, и предваряемые строчкой Did you mean:
рекомендаций, которые можно использовать "копипаст". Соответственно, цвета сигнализируют насколько найденные ошибки являются критическим, где красный является критической, а зеленый -- предупреждение.
2.2 Примеры исправления ошибок
SC2053
In vhdenv-plugins/aarch64--glibc-cross/rules/activate-rule line 110:
[[ $(basename "$a") == $2 ]] && return 1
^-- SC2053: Quote the right-hand side of == in [[ ]] to prevent glob matching.
Значение переменной $2 может быть скомкан, когда стоит справа от оператор сравнение =, == или != . Поэтому переменная "$2" должна быть обрамлена кавычками .
Результат. Корректируемый код:
- [[ $(basename "$a") == $2 ]] && return 1
+ [[ $(basename "$a") == "$2" ]] && return 1
SC1068
Пример сообщения :
VHDPLUGIN_STATUS = "$1"
^-- SC1068: Don't put spaces around the = in assignments (or quote to make it literal).
Результат. Корректируемый код:
-VHDPLUGIN_STATUS = "$1"
+VHDPLUGIN_STATUS="$1"
SC1078
Пример сообщения :
In bin/activate-run line 73:
if [ "$(basename $PWD)" != "vhd-env" ] ; then
^-- SC1078: Did you forget to close this double quoted string?
Результат. Корректируемый код:
**Невозможно привести, т.к. вторая окаймляющая кавычка была в другой языковой кодировки (ru_RU.UTF-8). Поэтому при переходе в корень рабочей директории плагина , должна устанавливаться к POSIX, как рекомендуют редакторы LFS .
SC1090
Пример сообщения :
In bin/activate-run line 83:
source "$VHDENV_LIBPATH"
^---------------^ SC1090: Can't follow non-constant source. Use a directive to specify location.
В результате, корректирующий код:
--- /path/to/storage/vhd-env/bin/activate-run 2025-01-31 21:46:40.094683100 +0300
+++ /path/to/repo/vhdenv.git/bin/activate-run 2025-01-31 21:52:42.352328417 +0300
@@ -79,7 +79,8 @@
vhd_deactivate
exit 1
elif [ -f "$VHDENV_ROOTDIR/bin/vhd-controls" ] ; then
- echo "${vhd_bold}VHD enviroment${vhd_normal}: $(basename "$VHDENV_LIBPATH") plug-in library"
+ echo "${vhd_bold}VHD enviroment${vhd_normal}: $(basename "$VHDENV_LIBPATH") plug-in library"
+ # shellcheck source=/dev/null
source "$VHDENV_LIBPATH"
if vhdenv_reinit ; then
vhdenv_deactivate
Для того, чтобы ShellCheck не обращал внимание на файл, указанный в $VHDENV_LIBPATH, указана директива # shellcheck source=/dev/null
.
SC1115
Пример сообщения :
In bin/vhdenv-install line 1:
# !/bin/bash
^-- SC1115: Remove spaces between # and ! in the shebang.
В результате, корректирующий код:
diff --git a/vhdenv-install b/vhdenv-install
index 4a1f593..cc85f6e 100644
--- a/vhdenv-install
+++ b/vhdenv-install
@@ -1,4 +1,4 @@
-#!/bin/bash
+# !/bin/bash
#
# The script provides to verify installed VHDEND binary
# if they are accorded to latest version of git repository
Между символом шебанга(#) и восклицательного знака был удален пробел.
SC2001
Пример сообщения:
In bin/vhd-controls line 268:
mount_ops="$(echo $VHDMOUNT_OPTIONS | sed -e 's/,user//g')"
^-- SC2001: See if you can use ${variable//search/replace} instead.
В результате, исправляющий код:
--- /path/to/storage/vhd-env/bin/vhd-controls 2025-02-01 16:13:58.546079300 +0300
+++ /path/to/repo/vhdenv.git/bin/vhd-controls 2025-02-01 18:28:23.694968296 +0300
@@ -265,7 +265,8 @@
mount_ops="$VHDMOUNT_OPTIONS"
if [[ $1 == mount ]] && [ "$2" == "--root-owner" ] ; then
echo "${vhd_bold}VHD enviroment :${vhd_normal} apply the root permission to $mpoint_name"
- mount_ops="$(echo $VHDMOUNT_OPTIONS | sed -e 's/,user//g')"
+# mount_ops="$(echo $VHDMOUNT_OPTIONS | sed -e 's/,user//g')"
+ mount_ops=${VHDMOUNT_OPTIONS/,user/}
fi
echo "${vhd_bold}Absoluted path/to/mount-point, mount options:${normal} $VHDMNT_DIRPATH/$mpoint_name"
if [[ $1 == mount ]] ; then
В данном случае, замена используется для удаления опции монтирования user
, когда осуществляется монтирование дисков VHD с правами суперпользователя:
vhdmount --root-owner native-linux && $VHDMNT_DIRPATH/native-linux
drwxr-xr-x 6 root root 4096 янв 19 20:27 .
drwxr-xr-x 1 user user 4096 фев 1 16:17 ..
drwx------ 2 root root 16384 янв 9 05:19 lost+found
и обычного пользователя:
vhdmount --root-owner native-linux && $VHDMNT_DIRPATH/native-linux
drwxr-xr-x 6 user user 4096 янв 19 20:27 .
drwxr-xr-x 1 user user 4096 фев 1 16:17 ..
drwx------ 2 root root 16384 янв 9 05:19 lost+found
Замена использования sed
в данном случае разумна и позволяет снизить затраты за счет использования простой замены ${variable//search/replace}
.
SC2004
In bin/vhd-controls line 463:
VHDFILE_BLOCKS=$(($VHDFILE_SIZE * (1024**2) / 4096 ))
^-----------^ SC2004: $/${} is unnecessary on arithmetic variables.
В результате, корректирующий код:
--- /path/to/storage/vhd-env/bin/vhd-controls 2025-02-02 10:09:48.758470200 +0300
+++ /path/to/repo/vhdenv.git/bin/vhd-controls 2025-02-02 10:14:28.472463774 +0300
@@ -459,7 +459,7 @@
_vhdenv_help
return $retfail
fi
- VHDFILE_BLOCKS=$(($VHDFILE_SIZE * (1024**2) / 4096 ))
+ VHDFILE_BLOCKS=$((VHDFILE_SIZE * (1024**2) / 4096 ))
echo "${vhd_bold}VHD enviroment:${vhd_normal} it is now, new file will be created"
echo " VHD file name: $(basename "$VHDFILE_PATH")"
echo " VHD file size: $VHDFILE_SIZE MBytes"
Символ доллара ($) перед VHDFILE_SIZE был избыточен, т.к. производится арифиметическая операция.
SC2012
Пример сообщения:
In bin/vhd-controls line 190:
vhdfns=$(ls $VHDFILE_DIRPATH/*.vhd | tr "\n" ",")
^-----------------------^ SC2012: Use find instead of ls to better handle non-alphanumeric filenames.
В результате, исправляющий код:
--- /path/to/storage/vhd-env/bin/vhd-controls 2025-02-01 13:44:39.935456600 +0300
+++ /path/to/repo/vhdenv.git/bin/vhd-controls 2025-02-01 13:49:13.678094593 +0300
@@ -187,7 +187,8 @@
fi
VHDFILE_PATH_SAVE="$VHDFILE_PATH"
export VHDFILE_PATH_SAVE
- vhdfns=$($(which ls) "$VHDFILE_DIRPATH/*.vhd" | tr "\n" ",")
+ vhdfns="$(find "$VHDFILE_DIRPATH" -type f -name "*.vhd" -print | tr "\n" ",")"
printf "%-16s : %-42s %-32s %-s\n" "DEVICE" "STAT" "VHD FILE" "MOUNT POINT"
IFS=,
for vhdfn in $vhdfns ;
Вместо команда ls рекомендуется использовать find на ряду с tr .
SC2025
Пример сообщения:
In bin/vhdbashrc line 6:
PS1="\e[0;31m[\u@\h \W]\$ \e[m "
^--------------------------^ SC2025: Make sure all escape sequences are enclosed in \[..\] to prevent line wrapping issues
Результат.Корректируемый код:
--- /path/to/storage/vhd-env/bin/vhdbashrc 2025-02-01 10:21:17.700376100 +0300
+++ /path/to/repo/vhdenv.git/bin/vhdbashrc 2025-02-01 10:26:13.917239349 +0300
@@ -3,7 +3,8 @@
-PS1="\e[0;31m[\u@\h \W]\$ \e[m "
+PS1="\[\e[0;31m[\u@\h \W]\$ \[\e[m\]"
+
set +h
umask 022
ESC-последовательность "\e[0;31m[\u@\h \W]$ \e[m " должна быть заключена в квадратные скобки, чтобы исключить проблем с нарушениями целостности строки. В данном случае, наблюдалось наложение букв вводимой команды на ранее введенную в командной строке:
[user@home ~]$ vhdenv_deactivav_deactivate
В результате чего, возникала ошибка нарушение целостности строки (Line wrapping) , которая проявлялась в виде вписывания части вводимого текста в предыдущий .
SC2027
Пример сообщения :
In bin/activate-run line 90:
PS1="("$VHDENV_PROMPT_PREFIX")$PS1"
^-------------------^ SC2027: The surrounding quotes actually unquote this. Remove or escape them.
Результат. Корректируемый код:
--- /path/to/storage/vhd-env/bin/activate-run 2025-01-31 21:32:12.109692200 +0300
+++ /path/to/repo/vhdenv.git/bin/activate-run 2025-01-31 21:46:32.258595628 +0300
@@ -87,7 +87,7 @@
echo "${vhd_bold}VHD enviroment${vhd_normal}: activated."
VHDENV_PROMPT_PREFIX="vhdenv"
_OLD_VHDENV_PS1="${PS1:-}"
- PS1="("$VHDENV_PROMPT_PREFIX")$PS1"
+ PS1="($VHDENV_PROMPT_PREFIX)$PS1"
export THIS_SCRIPT VHDENV_PROMPT_PREFIX _OLD_VHDENV_PS1 PS1
fi
else
Для устранения этой ошибки было достаточно удалить лишние двойные кавычки в PS1="("$VHDENV_PROMPT_PREFIX")$PS1"
.
SC2034
Пример сообщения:
In bin/activate line 19:
vhd_isready=true
^---------^ SC2034: vhd_isready appears unused. Verify use (or export if used externally).
Результат.Корректируемый код:
--- /path/to/storage/vhd-env/bin/activate 2025-01-31 10:18:51.315017800 +0300
+++ /path/to/vhdenv.git/bin/activate 2025-01-31 10:25:31.756845823 +0300
@@ -16,7 +16,6 @@
# So you need to use source command to the VHD enviroment to become active.
#
-vhd_isready=true
vhd_bold=$(tput bold)
vhd_normal=$(tput sgr0)
В данном случае, переменная vhd_isready явно не использовалась в исправляемом скрипте /path/to/storage/vhd-env/bin/activate и была удалена.
SC2046
Пример сообщения:
In bin/activate-run line 84:
elif [ -n "${VHDENV_ROOTDIR:-}" ] && [ -f $(pwd)/bin/vhd-controls ] ; then
^----^ SC2046: Quote this to prevent word splitting.
Корректируемый код:
@@ -81,7 +81,7 @@
echo "Please run this script in 'bin/activate's directory." >&2
vhd_deactivate
exit 1
- elif [ -n "${VHDENV_ROOTDIR:-}" ] && [ -f $(pwd)/bin/vhd-controls ] ; then
+ elif [ -n "${VHDENV_ROOTDIR:-}" ] && [ -f "$(pwd)"/bin/vhd-controls ] ; then
export VHDENV_ROOTDIR=$(pwd)
export VHDENV_LIBPATH=$VHDENV_ROOTDIR/bin/vhd-controls
export VHDENV_STORAGE=$(dirname $VHDENV_ROOTDIR)
shellcheck предупреждает об использовании read с опцией -r, потому что эта команда не будет включать функцию экранирования специальных символов (backslash function) :
read -p "Enter textual sequence -> " n1 ; echo $n1
Enter textual sequence -> aaaa \ bbbb
aaaa bbbb
read -r -p "Enter textual sequence -> " n1 ; echo $n1
Enter textual sequence -> aaaa \ bbbb
aaaa \ bbbb
SC2050
In bin/vhd-controls line 321:
if [[ $(type -t vhdplugin-activate) == function ]] && [ plugin_st != "X ." ] ; then
^-- SC2050: This expression is constant. Did you forget the $ on a variable?
Результат. Корректируемый код:
- if [ plugin_st != "X ." ] ; then
+ if [ "$plugin_st" != "X ." ] ; then
Перед именем переменной была пропущен $ . В тоже время нужно помнить, если будет пропущен так же "plugin_st" != "X ."
, то такое проверочное условие будет всегда истинным.
SC2070
Пример сообщения:
In bin/activate-run line 84:
elif [ -n ${VHDENV_ROOTDIR:-} ] && [ -f $(pwd)/bin/vhd-controls ] ; then
^-----------------^ SC2070: -n doesn't work with unquoted arguments. Quote or use [[ ]].
^-----------------^ SC2086: Double quote to prevent globbing and word splitting.
^----^ SC2046: Quote this to prevent word splitting.
Корректируемый код:
--- /path/to/storage/vhd-env/bin/activate-run 2025-01-31 17:33:56.470564100 +0300
+++ /path/to/vhdenv.git/bin/activate-run 2025-01-31 18:12:33.677659383 +0300
@@ -81,7 +81,7 @@
echo "Please run this script in 'bin/activate's directory." >&2
vhd_deactivate
exit 1
- elif [ -n ${VHDENV_ROOTDIR:-} ] && [ -f $(pwd)/bin/vhd-controls ] ; then
+ elif [ -n "${VHDENV_ROOTDIR:-}" ] && [ -f $(pwd)/bin/vhd-controls ] ; then
export VHDENV_ROOTDIR=$(pwd)
export VHDENV_LIBPATH=$VHDENV_ROOTDIR/bin/vhd-controls
export VHDENV_STORAGE=$(dirname $VHDENV_ROOTDIR)
SC2086
Пример сообщения:
In bin/activate-run line 71:
if [ "$(basename $PWD)" != "vhd-env" ] ; then
^--^ SC2086: Double quote to prevent globbing and word splitting.
Корректируемый код:
--- /path/to/storage/vhd-env/bin/activate-run 2025-01-31 16:55:29.819356300 +0300
+++ /path/to/vhdenv.git/bin/activate-run 2025-01-31 17:30:12.969198578 +0300
@@ -68,7 +68,7 @@
}
# checking on 'source' command usage and we are in vhd-env diroot
-if [ "$(basename $PWD)" != "vhd-env" ] ; then
+if [ "$(basename "$PWD")" != "vhd-env" ] ; then
echo "${vhd_bold}VHD enviroment error${vhd_normal}: you are out of the VHD enviroment."
vhd_isready=false
fi
Окаймлены кавычками переменные подставляемые значения переменных "$PWD" .
SC2116
Пример сообщения:
In bin/vhd-controls line 265:
mount_ops=$(echo "$VHDMOUNT_OPTIONS")
^-------------------------^ SC2116: Useless echo? Instead of 'cmd $(echo foo)', just use 'cmd foo'.
В результате, исправляющий код :
- mount_ops=$(echo "$VHDMOUNT_OPTIONS")
+ mount_ops="$VHDMOUNT_OPTIONS"
Исключения для SC2116
Иногда, этот шаблон используется используется из-за побочного эффекта команды echo или расширения. Для примера, здесь $(echo ..)
используется для расширения маски (glob):
glob="*.png"
files="$(echo $var)"
В таком виде echo не бесполезно, код является проблематичным, потому что он составлен из имен файлов, разделяемый пробелами, и это возможно будет нарушать имена файлов и других символов позднее во время повторного разбиения списка files. Поэтому лучиший способ будет:
Массивы:
files=( $glob );
echo "The first file is ${files[0]}"
Позиционные параметры:
set -- $glob;
echo "The first file is $1"
Отложенное разложение:
for file in $glob;
do
...
Все три метода позволят избежать проблем со специальными символами в именах файлов.
Другим примером $(echo ..) является применения расширения ESC-последовательностей:
unexpanded='var\tvalue'
expanded="$(echo "$var")"
В этом случае, используйте взамен printf , который прекрасно ладит с ESC-последовательностями.
В конечном счете, если есть желание составить ряд символов через символ пробела, следует это делать лучше с for или printf :
printf '%s\n' $glob
SC2115
Пример сообщения:
In bin/vhd-controls line 429:
[ -d "$VHDMNT_DIRPATH/$mpoint_name" ] && rm -rf "$VHDMNT_DIRPATH/$mpoint_name";
^----------------------------^ SC2115: Use "${var:?}" to ensure this never expands to / .
В результате, исправляющий код :
--- /path/to/storage/vhd-env/bin/vhd-controls 2025-02-02 04:16:33.738959200 +0300
+++ /path/to/repo/vhdenv.git/bin/vhd-controls 2025-02-02 04:28:45.588546549 +0300
@@ -426,7 +426,7 @@
break
done
- if rm -rf "$VHDFILE_PATH" ; then
+ if rm -rf "${VHDFILE_PATH:?}" ; then
- [ -d "$VHDMNT_DIRPATH/$mpoint_name" ] && rm -rf "$VHDMNT_DIRPATH/$mpoint_name";
+ [ -d "$VHDMNT_DIRPATH/$mpoint_name" ] && rm -rf "${VHDMNT_DIRPATH:?}/$mpoint_name";
echo "${vhd_bold}VHD enviroment:${vhd_normal} $VHDFILE was succesfully deleted."
fi
;;
Использование ${VHDMNT_DIRPATH:?}
предотвратит удаление из корня RootFS точки монтирования $mpoint_name
, когдаVHDMNT_DIRPATH
не присвоено значение.
SC2120
Пример сообщения:
In bin/activate line 22:
vhdbash_newinst_exec(){
^-- SC2120: vhdbash_newinst_exec references arguments, but none are ever passed.
Корректируемый код:
--- /path/to/storage/vhd-env/bin/activate 2025-01-31 10:28:36.824860300 +0300
+++ /path/to/vhdenv.git/bin/activate 2025-01-31 10:35:50.561017097 +0300
@@ -37,7 +38,7 @@
echo "${vhd_bold}VHD enviroment${vhd_normal}: activity yet!"
else
if [ -f bin/activate-run ] ; then
- vhdbash_newinst_exec
+ vhdbash_newinst_exec "$@"
else
echo "vhdenv error: You are out of the VHD enviroment."
fi
В функции, $1 и т.д. относится к параметрам функции, но скрипт использовался без параметров, поэтому в точке вызова был добавлен передаваемый аргумент "$@"
SC2140 и SC2180
In bash-associative-arr.sh line 67:
value="${ass2d_arr["0,1"]["value"]}"
^--------------------------^ SC2180: Bash does not support multidimensional arrays. Use 1D or associative arrays.
^-- SC2140: Word is of the form "A"B"C" (B indicated). Did you mean "ABC" or "A\"B\"C"?
Bash не поддерживает многомерные массивы и потому сопровождается SC2140, которая путается в количестве указанных двойных кавычек:
declare -A ass2d_arr
...
ass2d_arr["0,1"]=(["value"]="b" ["col"]="0" ["line"]="1")
...
value="${${ass2d_arr["0,1"]}["value"]}"
echo "The value for the element is: $value"
SC2141
In bin/vhdenv-install line 66:
IFS=Backup_of_internal_field_separator
^-- SC2141: This IFS value contains the literal letter 'n'. For tabs/linefeeds/escapes, use $'..', literal, or printf.
В результате, исправляющий код:
- Backup_of_internal_field_separator=IFS
+ Backup_of_internal_field_separator=$IFS
SC2143
In bin/vhd-controls line 338:
if [ -z "$(losetup -l | grep "$VHDFILE_PATH")" ] ; then
^-- SC2143: Use ! grep -q instead of comparing output with [ -z .. ].
В результате, исправляющий код:
--- /path/to/storage/vhd-env/bin/vhd-controls 2025-02-01 19:25:17.758393500 +0300
+++ /path/to/repo/vhdenv.git/bin/vhd-controls 2025-02-01 19:28:05.310582078 +0300
@@ -335,7 +335,8 @@
fi
;;
mount)
- if [ -z "$(losetup -l | grep "$VHDFILE_PATH")" ] ; then
+# if [ -z "$(losetup -l | grep "$VHDFILE_PATH")" ] ; then
+ if ! losetup -l | grep -q "$VHDFILE_PATH" ; then
vhdenv_ldev=$(losetup -f)
if [ ! -b $vhdenv_ldev ] ; then
echo "${vhd_bold}VHD enviroment:${vhd_normal} can't find the first unused loop device."
Позволяет повысить эффективность за счет количества итераций grep до первого совпадения, снижаемых используемой опцией -q
.
SC2145
In bash-associative-arr.sh line 33:
echo "All ordered pairs: ${!mpairs[@]}"
^-----------^ SC2145: Argument mixes string and array. Use * or separate argument.
В принципе bash выполнит ${!mpairs[@]}
, но shellcheck считает это грубой ошибкой, поэтому лучше вместо '${!mpairs[@]}'
использовать :
print "All values are associated with ordered pairs:"
for pair in "${!mpairs[@]}" ;
do
print "%s " "${mpairs[pair]}"
done
echo
SC2148
Результат. Корректируемый код:
--- /path/to/storage/vhd-env/bin/activate-run 2025-01-31 07:54:57.907348900 +0300
+++ /path/to/vhdenv.git/bin/activate-run 2025-01-31 13:50:23.554165390 +0300
@@ -1,3 +1,4 @@
+#!/bin/bash
#This file must be used with "source vhd-env/bin/activate" from bash
#You cannot run it directly.
В первую строку cкрипта activate добавлен шебанг #!/bin/bash
.
SC2152
Пример сообщения:
In bin/activate line 25:
return retfail
^-----^ SC2152: Can only return 0-255. Other data should be written to stdout.
...
In bin/activate line 29:
return retok
^---^ SC2152: Can only return 0-255. Other data should be written to stdout.
Результат. Корректируемый код:
vhdbash_newinst_exec(){
if [ ! -f "$1" ] ; then
echo "${vhd_bold}VHD enviroment error${vhd_normal}: bashrc file's path does not accepted."
- return retfail
+ return 1
fi
if [ -f bin/activate-run ] && [ -f "$1" ] ; then
if sudo -u "$USER" VHDENV_BASHRCFILE="$1" bash bin/vhdbash-newinst-exec ; then
- return retok
+ return 0
fi
fi
- return retfail
+ return 1
}
SC2154
In bin/vhd-controls line 271:
echo "${vhd_bold}Absoluted path/to/mount-point, mount options:${normal} $VHDMNT_DIRPATH/$mpoint_name"
^-------^ SC2154: normal is referenced but not assigned.
В результате, исправляющий код:
--- /path/to/storage/vhd-env/bin/vhd-controls 2025-02-01 18:30:59.440021600 +0300
+++ /path/to/repo/vhdenv.git/bin/vhd-controls 2025-02-01 18:54:46.485920568 +0300
@@ -268,7 +268,7 @@
# mount_ops="$(echo $VHDMOUNT_OPTIONS | sed -e 's/,user//g')"
mount_ops=${VHDMOUNT_OPTIONS/,user/}
fi
- echo "${vhd_bold}Absoluted path/to/mount-point, mount options:${normal} $VHDMNT_DIRPATH/$mpoint_name"
+ echo "${vhd_bold}Absoluted path/to/mount-point, mount options:${vhd_normal} $VHDMNT_DIRPATH/$mpoint_name"
if [[ $1 == mount ]] ; then
echo "${vhd_bold}Mount options:${vhd_normal} $VHDMNT_DIRPATH/$mpoint_name, $mount_ops"
fi
Ссылка не существующую на переменную normal была допущена при переименовании имени к vhd_normal .
SC2155
In bin/activate-run line 85:
export VHDENV_ROOTDIR=$(pwd)
^------------^ SC2155: Declare and assign separately to avoid masking return values
In bin/activate-run line 87:
export VHDENV_STORAGE=$(dirname $VHDENV_ROOTDIR)
^------------^ SC2155: Declare and assign separately to avoid masking return values.
В результате, исправляющий код:
--- /path/to/storage/vhd-env/bin/activate-run 2025-01-31 18:44:13.745303200 +0300
+++ /path/to/vhdenv.git/bin/activate-run 2025-01-31 18:48:47.967311927 +0300
@@ -82,9 +82,10 @@
vhd_deactivate
exit 1
elif [ -n "${VHDENV_ROOTDIR:-}" ] && [ -f "$(pwd)"/bin/vhd-controls ] ; then
- export VHDENV_ROOTDIR=$(pwd)
- export VHDENV_LIBPATH=$VHDENV_ROOTDIR/bin/vhd-controls
- export VHDENV_STORAGE=$(dirname "$VHDENV_ROOTDIR")
+ VHDENV_ROOTDIR=$(pwd)
+ VHDENV_LIBPATH=$VHDENV_ROOTDIR/bin/vhd-controls
+ VHDENV_STORAGE=$(dirname "$VHDENV_ROOTDIR")
+ export VHDENV_ROOTDIR VHDENV_LIBPATH VHDENV_STORAGE
echo "${vhd_bold}VHD enviroment${vhd_normal}: $(basename $VHDENV_LIBPATH) plug-in library"
source $VHDENV_LIBPATH
if vhdenv_reinit ; then
Декларация в качестве внешней переменной должно производится отдельно после присвоения значения переменной. Для локальной переменной эта ошибка может проявляться тоже :
In bin/vhd-controls line 298:
local vhdmnts=$(vhdfindmnt | sed -n "s/^.*mnt\/\(.*\)[[:space:]]\/dev.*$/\1/p" | sed "s/^[[:space:]]*//" | cut -d ' ' -f1 | tr "\n" ",")
^-----^ SC2155: Declare and assign separately to avoid masking return values.
В результате, исправляющий код:
--- /home/user/media/JMS678-satadrv/vhd-env/bin/vhd-controls 2025-02-01 18:54:54.243970800 +0300
+++ /path/to/repo/vhdenv.git/bin/vhd-controls 2025-02-01 19:08:04.970265993 +0300
@@ -295,7 +295,8 @@
printf "%-6s %-24s %s\n" "STATUS" "PLUGIN" "DESCRIPTION"
fi
local plugin_st="X ."
- local vhdmnts=$(vhdfindmnt | sed -n "s/^.*mnt\/\(.*\)[[:space:]]\/dev.*$/\1/p" | sed "s/^[[:space:]]*//" | cut -d ' ' -f1 | tr "\n" ",")
+ local vhdmnts
+ vhdmnts="$(vhdfindmnt | sed -n "s/^.*mnt\/\(.*\)[[:space:]]\/dev.*$/\1/p" | sed "s/^[[:space:]]*//" | cut -d ' ' -f1 | tr "\n" ",")"
IFS=,
for vhdmnt in $vhdmnts ;
do
SC2162
In bin/vhd-controls line 418:
read -p "Are you sure? Are you sure? Do you want to remove $VHDFILE file? (y/n):" your_answer
^--^ SC2162: read without -r will mangle backslashes.
В результате, исправляющий код:
--- /path/to/storage/vhd-env/bin/vhd-controls 2025-02-01 21:59:42.822340100 +0300
+++ /path/to/repo/vhdenv.git/bin/vhd-controls 2025-02-02 03:43:59.563967471 +0300
@@ -415,7 +415,7 @@
fi
fi
while true ; do
- read -p "Are you sure? Are you sure? Do you want to remove $VHDFILE file? (y/n):" your_answer
+ read -r -p "Are you sure? Are you sure? Do you want to remove $VHDFILE file? (y/n):" your_answer
if [ "$your_answer" != "y" ] && [ "$your_answer" != "n" ] ; then
continue
fi
Он будет предотвращать чтение управляющих символов в виде esc-последовательности:
Are you sure? Can a file creating continue?(y/n):^[[A^[[A
SC2164
In vhdenv-plugins/aarch64--glibc-cross/rules/activate-rule line 117:
cd ~
^--^ SC2164: Use 'cd ... || exit' or 'cd ... || return' in case cd fails.
Результат. Корректируемый код:
151d153
< cd ~
166a169
> cd "$HOME" || return
170,171c173,174
Рекомендуется использовать cd "$HOME" || return
, потому что команда cd может завершиться с отказом из-за неправильно указанного пути, имени файла или директории, отсутсвия прав доступа, сломанной ссылки и т.д.
SC2181
Пример сообщения:
In bin/vhd-controls line 261:
if [ $? -ne 0 ] ; then
^-- SC2181: Check exit code directly with e.g. 'if mycmd;', not indirectly with $?.
Полученный в результате код:
--- /path/to/storage/vhd-env/bin/vhd-controls 2025-02-01 14:15:39.859102200 +0300
+++ /path/to/repo/vhdenv.git/bin/vhd-controls 2025-02-01 15:10:28.269944186 +0300
@@ -257,8 +257,7 @@
echo "${vhd_bold}VHD enviroment :${vhd_normal} can't create the mount point: $VHDMNT_DIRPATH/$mpoint_name"
return $retfail
fi
- sudo chown "$(id -u)":"$(id -g)" "$VHDMNT_DIRPATH"/"$mpoint_name"
- if [ $? -ne 0 ] ; then
+ if ! sudo chown "$(id -u)":"$(id -g)" "$VHDMNT_DIRPATH"/"$mpoint_name" ; then
return $retfail
fi
Вместо косвенного if [ $? -ne 0 ] ;
, широко используемого ранее, используется прямой способа определения результата выполнения команды.
Оценка нормального выполнения команды mycmd
if mycmd ; then
echo " mycmd was succefully done."
fi
Оценка нормального выполнения команды mycmd
if ! mycmd ; then
echo " mycmd has returned an abnormal value"
fi
Примечание. Чтобы "не схватить" ошибку SC2152 возвращаемое значение командой должно быть в диапазоне 0,...,255 . При этом, возвращаемое нулевое значение будет являться "нормальным" возвращаемым значением, а все что больше или равно 1 и т.д. -- аномальным.
SC2209
In bin/vhd-controls line 354:
mycmd=eval "sudo -- mount $vhdenv_ldev $VHDMNT_DIRPATH/$mpoint_name -o $mount_ops"
^-- SC2209: Use var=$(command) to assign output (or quote to assign string).
В результате, исправляющий код:
--- /path/to/storage/vhd-env/bin/vhd-controls 2025-02-01 21:00:28.170068700 +0300
+++ /path/to/repo/vhdenv.git/bin/vhd-controls 2025-02-01 21:02:34.796532787 +0300
@@ -351,8 +351,8 @@
return $retfail
fi
- local mycmd
- mycmd=eval "sudo -- mount $vhdenv_ldev $VHDMNT_DIRPATH/$mpoint_name -o $mount_ops"
- if mycmd ; then
+ if eval "sudo -- mount $vhdenv_ldev $VHDMNT_DIRPATH/$mpoint_name -o $mount_ops" ; then
eval "sudo losetup --detach $vhdenv_ldev"
echo "${vhd_bold}VHD enviroment error:${vhd_normal} can't associate $vhdenv_ldev's loop device with $(basename $VHDFILE_PATH) file."
return $retfail
Вместо использования локальной переменной mycmd=$(eval "sudo -- ...") выполняемую команду подставил в if-statement
, как показано выше.
SC2236
Пример сообщения :
In bin/activate line 35:
if [ ! -z "${VHDENV_STORAGE:-}" ] ; then
^-- SC2236: Use -n instead of ! -z.
Результат. Корректируемый код:
--- /path/to/storage/vhd-env/bin/activate 2025-01-31 11:48:00.362123400 +0300
+++ /path/to/repo/vhdenv.git/bin/activate 2025-01-31 11:54:50.424221414 +0300
@@ -32,7 +32,7 @@
return 1
}
-if [ ! -z "${VHDENV_STORAGE:-}" ] ; then
+if [ -n "${VHDENV_STORAGE:-}" ] ; then
echo "${vhd_bold}VHD enviroment${vhd_normal}: activity yet!"
else
if [ -f bin/activate-run ] ; then
Произведена замена ! -z "${VHDENV_STORAGE:-}"
на ! -z "${VHDENV_STORAGE:-}"
.